Fix: tab switching hangs on iOS Safari

- showView: requestAnimationFrame + мгновенный loader (UI не висит)
- maybeOnboard на старте fire-and-forget (не блокирует init)
- MapView: НЕ запрашивает геолокацию автоматически (только по кнопке)
- MapView: правильно destroy map если container удалён (нет утечки Leaflet)
- invalidateSize() перед setView чтобы тайлы 2GIS догрузились
This commit is contained in:
root 2026-08-20 21:28:33 +00:00
parent 000206426f
commit 06c8f8b593
2 changed files with 37 additions and 14 deletions

View File

@ -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 = '<div class="view">Неизвестный экран</div>';
// мгновенный placeholder — чтоб UI не висел при тяжёлом render
if (root) {
root.innerHTML = '<div class="view" style="display:flex;justify-content:center;padding:40px;"><div class="loader"></div></div>';
}
// 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 = '<div class="view">Неизвестный экран</div>';
}
});
},
openChat(chatId, otherId, otherName) {

View File

@ -48,6 +48,21 @@ const MapView = {
</div>
`;
// если старый 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() {