Files
app-v2/src/App.vue
2025-10-28 10:04:13 +03:00

178 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import SpaceList from "@/components/space-list/SpaceList.vue";
import Toolbar from "@/components/Toolbar.vue";
import Toast from "primevue/toast";
import { useSpaceStore } from "@/stores/spaceStore";
import { useToolbarStore } from "@/stores/toolbar-store";
import router from "@/router";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { computed, onMounted, onBeforeUnmount, ref, watch } from "vue";
import {useToast} from "primevue/usetoast";
const spaceStore = useSpaceStore();
const toolbarStore = useToolbarStore();
const route = useRoute();
const platform = ref<string>("unknown")
const toast = useToast();
const tgApp = (window as any)?.Telegram?.WebApp;
const isTelegram = computed(() => !!tgApp);
const isSpaceSelectorVisible = ref(false);
const isSpaceSelected = computed(
() => spaceStore.selectedSpaceId === undefined || isSpaceSelectorVisible.value
);
const menu = [
{ name: "Dashboard", icon: "pi pi-chart-bar", link: "/", navStack: 'dashboard' },
{ name: "Transactions", icon: "pi pi-cog", link: "/transactions", navStack: 'transactions' },
{ name: "Settings", icon: "pi pi-list", link: "/settings", navStack: 'settings' },
];
function spaceSelected() {
router.push("/");
isSpaceSelectorVisible.value = false;
}
let backHandler: (() => void) | null = null;
function setupBackButton() {
if (!tgApp.initData) return;
if (route.path !== "/") {
tgApp.BackButton.show();
// снять старый обработчик
if (backHandler) tgApp.BackButton.offClick(backHandler);
// навесить новый
backHandler = () => {
if (window.history.length > 1) {
router.back();
} else {
tgApp.BackButton.hide();
}
};
tgApp.BackButton.onClick(backHandler);
} else {
tgApp.BackButton.hide();
}
}
const isNavVisible = ref(true)
const isInputFocused = ref(false)
const handleFocusIn = () => {
isNavVisible.value = false
isInputFocused.value = true
}
const handleFocusOut = () => {
isNavVisible.value = true
isInputFocused.value = false
}
const blurAllInputs = () => {
// Снимаем фокус со всех активных элементов
const activeElement = document.activeElement as HTMLElement
if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')) {
activeElement.blur()
}
}
onMounted(() => {
toolbarStore.registerHandler("openSpacePicker", () => {
isSpaceSelectorVisible.value = true;
});
document.addEventListener('focusin', handleFocusIn)
document.addEventListener('focusout', handleFocusOut)
if (tgApp.initData) {
try {
tgApp.expand?.();
platform.value = tgApp.platform
if (['ios', 'android'].includes(platform.value)) {
tgApp.requestFullscreen?.();
}
tgApp.lockOrientation?.();
tgApp.disableVerticalSwipes()
tgApp.ready();
setupBackButton();
} catch (err) {
console.warn("Telegram WebApp init error:", err);
}
}
});
// 🔁 следим за изменением маршрута
watch(
() => route.path,
() => setupBackButton()
);
onBeforeUnmount(() => {
toolbarStore.unregisterHandler("openSpacePicker");
document.removeEventListener('focusin', handleFocusIn)
document.removeEventListener('focusout', handleFocusOut)
tgApp?.BackButton?.hide();
if (backHandler) tgApp?.BackButton?.offClick(backHandler);
});
</script>
<template>
<Toast/>
<!-- {{platform}}-->
<!-- {{['ios', 'android'].includes(platform) }}-->
<div class="flex flex-col tg " :class="['ios', 'android'].includes(platform) ? '!pt-10' : ''">
<SpaceList v-if="isSpaceSelected" @space-selected="spaceSelected" />
<div v-else class="flex flex-col w-full gap-4">
<div class="w-full flex flex-row items-end justify-end pt-2 pe-4">
<Toolbar />
</div>
<div class="flex flex-col w-full h-full items-end px-4 gap-4 pb-6">
<router-view class="w-full" />
</div>
<button
v-if="isInputFocused"
@click="blurAllInputs"
class="fixed bottom-4 right-4 z-50 bg-blue-500 text-white px-4 py-2 rounded-lg shadow-lg"
>
Готово
</button>
<nav v-if="isNavVisible"
class="fixed inset-x-0 bottom-4 z-50 w-full flex justify-center items-center"
style="padding-bottom: var(--tg-content-safe-area-inset-bottom) !important;"
>
<div class="flex h-full items-center justify-between py-2 bg-white rounded-4xl px-6 w-fit">
<!-- <div class="flex h-full justify-items-center items-center justify-between py-2 bg-white rounded-4xl !px-6 w-fit">-->
<router-link
v-for="item in menu"
:key="item.link"
:to="item.link"
class="flex w-fit h-full flex-col items-center gap-2 !py-2 !px-4"
:class="route.meta.navStack === item.navStack ? 'bg-green-100 rounded-2xl ' : ''"
>
<i class="!text-lg" :class="item.icon" />
<span class="font-medium text-gray-900">{{ item.name }}</span>
</router-link>
</div>
</nav>
<div class="h-16" />
</div>
</div>
</template>
<style scoped>
.tg {
width: 100% !important;
margin:
var(--tg-content-safe-area-inset-top)
var(--tg-content-safe-area-inset-right)
0
var(--tg-content-safe-area-inset-left) !important;
}
</style>