tg app fix sizes
This commit is contained in:
73
src/App.vue
73
src/App.vue
@@ -1,18 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SpaceList from "@/components/space-list/SpaceList.vue";
|
import SpaceList from "@/components/space-list/SpaceList.vue";
|
||||||
import { useSpaceStore } from "@/stores/spaceStore";
|
|
||||||
import { computed, onMounted, onBeforeUnmount, ref } from "vue";
|
|
||||||
import Toolbar from "@/components/Toolbar.vue";
|
import Toolbar from "@/components/Toolbar.vue";
|
||||||
|
import { useSpaceStore } from "@/stores/spaceStore";
|
||||||
import { useToolbarStore } from "@/stores/toolbar-store";
|
import { useToolbarStore } from "@/stores/toolbar-store";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import {useRoute} from "vue-router"; // если у тебя index.ts экспортирует по умолчанию, можно без /index.js
|
import { useRoute, onBeforeRouteUpdate } from "vue-router";
|
||||||
|
import { computed, onMounted, onBeforeUnmount, ref, watch } from "vue";
|
||||||
|
|
||||||
const spaceStore = useSpaceStore();
|
const spaceStore = useSpaceStore();
|
||||||
const toolbarStore = useToolbarStore();
|
const toolbarStore = useToolbarStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
// true/false, есть ли Telegram WebApp
|
const tgApp = (window as any)?.Telegram?.WebApp;
|
||||||
const isTelegram = computed(() => !!(window as any)?.Telegram?.WebApp);
|
const isTelegram = computed(() => !!tgApp);
|
||||||
|
|
||||||
const isSpaceSelectorVisible = ref(false);
|
const isSpaceSelectorVisible = ref(false);
|
||||||
const isSpaceSelected = computed(
|
const isSpaceSelected = computed(
|
||||||
@@ -30,46 +30,63 @@ function spaceSelected() {
|
|||||||
isSpaceSelectorVisible.value = false;
|
isSpaceSelectorVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let backHandler: (() => void) | null = null;
|
||||||
|
|
||||||
|
function setupBackButton() {
|
||||||
|
if (!tgApp) 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
toolbarStore.registerHandler("openSpacePicker", () => {
|
toolbarStore.registerHandler("openSpacePicker", () => {
|
||||||
isSpaceSelectorVisible.value = true;
|
isSpaceSelectorVisible.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const tgApp = (window as any)?.Telegram?.WebApp;
|
|
||||||
if (tgApp) {
|
if (tgApp) {
|
||||||
try {
|
try {
|
||||||
|
tgApp.expand?.();
|
||||||
tgApp.expand?.(); // более мягкий вариант, чем requestFullscreen()
|
|
||||||
tgApp.requestFullscreen?.();
|
tgApp.requestFullscreen?.();
|
||||||
tgApp.lockOrientation?.();
|
tgApp.lockOrientation?.();
|
||||||
if (route.path !== '/') {
|
|
||||||
tgApp.BackButton.show()
|
|
||||||
// при нажатии — возвращаемся назад
|
|
||||||
const handleBack = () => {
|
|
||||||
// например, переход на предыдущий маршрут
|
|
||||||
if (window.history.length > 1) {
|
|
||||||
router.back();
|
|
||||||
} else {
|
|
||||||
tg.BackButton.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tg.BackButton.onClick(handleBack);
|
|
||||||
}
|
|
||||||
tgApp.ready();
|
tgApp.ready();
|
||||||
} catch {
|
setupBackButton();
|
||||||
/* ignore */
|
} catch (err) {
|
||||||
|
console.warn("Telegram WebApp init error:", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 🔁 следим за изменением маршрута
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
() => setupBackButton()
|
||||||
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
toolbarStore.unregisterHandler("openSpacePicker");
|
toolbarStore.unregisterHandler("openSpacePicker");
|
||||||
|
tgApp?.BackButton?.hide();
|
||||||
|
if (backHandler) tgApp?.BackButton?.offClick(backHandler);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div class="flex flex-col tg">
|
<div class="flex flex-col tg">
|
||||||
<SpaceList v-if="isSpaceSelected" @space-selected="spaceSelected" />
|
<SpaceList v-if="isSpaceSelected" @space-selected="spaceSelected" />
|
||||||
|
|
||||||
@@ -91,25 +108,23 @@ onBeforeUnmount(() => {
|
|||||||
class="flex w-fit flex-col items-center gap-2"
|
class="flex w-fit flex-col items-center gap-2"
|
||||||
>
|
>
|
||||||
<i class="!text-lg" :class="item.icon" />
|
<i class="!text-lg" :class="item.icon" />
|
||||||
<span class=" font-medium text-gray-900">{{ item.name }}</span>
|
<span class="font-medium text-gray-900">{{ item.name }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- отступ под нижний navbar, чтобы контент не перекрывался -->
|
|
||||||
<div class="h-16" />
|
<div class="h-16" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Порядок отступов: top right bottom left */
|
|
||||||
.tg {
|
.tg {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
padding:
|
padding:
|
||||||
var(--tg-content-safe-area-inset-top)
|
var(--tg-content-safe-area-inset-top)
|
||||||
var(--tg-content-safe-area-inset-right)
|
var(--tg-content-safe-area-inset-right)
|
||||||
var(--tg-content-safe-area-inset-bottom)
|
0
|
||||||
var(--tg-content-safe-area-inset-left) !important;
|
var(--tg-content-safe-area-inset-left) !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -59,7 +59,7 @@ onMounted(async () => {
|
|||||||
<div class="flex-row"> {{ categories[key].icon }} {{ categories[key].name }}</div>
|
<div class="flex-row"> {{ categories[key].icon }} {{ categories[key].name }}</div>
|
||||||
<div class="flex flex-row text-sm">{{ categories[key].description }}</div>
|
<div class="flex flex-row text-sm">{{ categories[key].description }}</div>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-angle-right !text-xl !font-extralight"/>
|
<i class="pi pi-angle-right !font-extralight"/>
|
||||||
</div>
|
</div>
|
||||||
<Divider v-if="key+1 !== categories.length" class="!m-0 !py-3"/>
|
<Divider v-if="key+1 !== categories.length" class="!m-0 !py-3"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ onMounted(async () => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-items-start">
|
<div class="flex flex-col w-full justify-items-start">
|
||||||
<label class="!font-semibold text-2xl text-gray-600 pl-2">Category name</label>
|
<label class="!font-semibold text-gray-600 pl-2">Category name</label>
|
||||||
<div class="card !justify-start !items-start !p-4 !pl-5 ">
|
<div class="card !justify-start !items-start !p-4 !pl-5 ">
|
||||||
<input class="font-extralight text-xl w-full focus:outline-0" placeholder="Name" v-model="categoryName"/>
|
<input class="font-extralight w-full focus:outline-0" placeholder="Name" v-model="categoryName"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-items-start">
|
<div class="flex flex-col w-full justify-items-start">
|
||||||
<label class="!font-semibold text-2xl text-gray-600 !pl-2">Category description</label>
|
<label class="!font-semibold text-gray-600 !pl-2">Category description</label>
|
||||||
<div class="card !justify-start !items-start !pl-2">
|
<div class="card !justify-start !items-start !pl-2">
|
||||||
<textarea
|
<textarea
|
||||||
class="font-extralight text-xl w-full focus:outline-0 !focus:border-0 !@focus:shadow-none !bg-white !border-0 min-h-36"
|
class="font-extralight w-full focus:outline-0 !focus:border-0 !@focus:shadow-none !bg-white !border-0 min-h-36"
|
||||||
style="box-shadow: none !important;"
|
style="box-shadow: none !important;"
|
||||||
placeholder="Description" v-model="categoryDescription"/>
|
placeholder="Description" v-model="categoryDescription"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ onMounted(async () => {
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div v-for="key in recurrents.keys()" :key="recurrents[key].id"
|
<div v-for="key in recurrents.keys()" :key="recurrents[key].id"
|
||||||
@click="router.push(`/recurrents/${recurrents[key].id}/edit`)"
|
@click="router.push(`/recurrents/${recurrents[key].id}/edit`)"
|
||||||
class="flex flex-col w-full gap-0 pl-5 items-start justify-items-center font-bold text-xl">
|
class="flex flex-col w-full gap-0 pl-5 items-start justify-items-center font-bold ">
|
||||||
<div class="flex-row w-full items-center justify-between">
|
<div class="flex-row w-full items-center justify-between">
|
||||||
<div class="flex-row items-center gap-2">
|
<div class="flex-row items-center gap-2">
|
||||||
<span class="text-4xl">{{ recurrents[key].category.icon }}</span>
|
<span class="text-4xl">{{ recurrents[key].category.icon }}</span>
|
||||||
@@ -67,7 +67,7 @@ onMounted(async () => {
|
|||||||
<span class="text-lg !font-semibold">{{recurrents[key].amount}}₽ </span>
|
<span class="text-lg !font-semibold">{{recurrents[key].amount}}₽ </span>
|
||||||
<span class="text-sm">каждое {{ recurrents[key].date }} число </span>
|
<span class="text-sm">каждое {{ recurrents[key].date }} число </span>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-angle-right !text-xl !font-extralight"/>
|
<i class="pi pi-angle-right !font-extralight"/>
|
||||||
</div>
|
</div>
|
||||||
<Divider v-if="key+1 !== recurrents.length" class="!m-0 !py-3"/>
|
<Divider v-if="key+1 !== recurrents.length" class="!m-0 !py-3"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ onMounted(async () => {
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div v-if="categories.length===0" class="card !gap-4 !p-10">
|
<div v-if="categories.length===0" class="card !gap-4 !p-10">
|
||||||
<span class="text-2xl">No categories available.</span>
|
<span class="">No categories available.</span>
|
||||||
<span class="text-center">Maybe you want to <router-link to="/categories" class="!text-blue-700">create a new category</router-link> first?</span>
|
<span class="text-center">Maybe you want to <router-link to="/categories" class="!text-blue-700">create a new category</router-link> first?</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex flex-col w-full justify-items-start gap-7">
|
<div v-else class="flex flex-col w-full justify-items-start gap-7">
|
||||||
@@ -115,7 +115,7 @@ onMounted(async () => {
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div v-for="key in categories.keys()" :key="categories[key].id"
|
<div v-for="key in categories.keys()" :key="categories[key].id"
|
||||||
@click="recurrentCategory = categories[key]; isCategorySelectorOpened = false"
|
@click="recurrentCategory = categories[key]; isCategorySelectorOpened = false"
|
||||||
class="flex flex-col w-full gap-0 pl-5 items-start justify-items-center font-bold text-xl">
|
class="flex flex-col w-full gap-0 pl-5 items-start justify-items-center font-bold ">
|
||||||
<div class="flex-row w-full items-center justify-between">
|
<div class="flex-row w-full items-center justify-between">
|
||||||
<div class="flex-row items-center gap-2">
|
<div class="flex-row items-center gap-2">
|
||||||
<span class="text-3xl">{{ categories[key].icon }} </span>
|
<span class="text-3xl">{{ categories[key].icon }} </span>
|
||||||
@@ -124,7 +124,7 @@ onMounted(async () => {
|
|||||||
<div class="flex flex-row text-sm">{{ categories[key].description }}</div>
|
<div class="flex flex-row text-sm">{{ categories[key].description }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-angle-right !text-xl !font-extralight"/>
|
<i class="pi pi-angle-right !font-extralight"/>
|
||||||
</div>
|
</div>
|
||||||
<Divider v-if="key+1 !== categories.length" class="!m-0 !py-3"/>
|
<Divider v-if="key+1 !== categories.length" class="!m-0 !py-3"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,28 +152,28 @@ onMounted(async () => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-items-start">
|
<div class="flex flex-col w-full justify-items-start">
|
||||||
<label class="!font-semibold text-2xl text-gray-600 pl-2">Recurrent category</label>
|
<label class="!font-semibold text-gray-600 pl-2">Recurrent category</label>
|
||||||
<div class="card !justify-start !items-start !p-4 !pl-5 " @click="isCategorySelectorOpened = true">
|
<div class="card !justify-start !items-start !p-4 !pl-5 " @click="isCategorySelectorOpened = true">
|
||||||
<div class="flex-row w-full gap-2 items-center justify-between">
|
<div class="flex-row w-full gap-2 items-center justify-between">
|
||||||
<div class="flex-row gap-2 items-center">
|
<div class="flex-row gap-2 items-center">
|
||||||
<span class="!text-3xl ">{{ recurrentCategory.icon }}</span>
|
<span class="!text-3xl ">{{ recurrentCategory.icon }}</span>
|
||||||
<div class="flex-col ">
|
<div class="flex-col ">
|
||||||
<span class=" !text-2xl">{{ recurrentCategory.name }}
|
<span class=" !">{{ recurrentCategory.name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-angle-right !text-xl !font-extralight"/>
|
<i class="pi pi-angle-right !font-extralight"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-items-start">
|
<div class="flex flex-col w-full justify-items-start">
|
||||||
<label class="!font-semibold text-2xl text-gray-600 pl-2">Recurrent name</label>
|
<label class="!font-semibold text-gray-600 pl-2">Recurrent name</label>
|
||||||
<div class="card !justify-start !items-start !p-4 !pl-5 ">
|
<div class="card !justify-start !items-start !p-4 !pl-5 ">
|
||||||
<input class="font-extralight text-xl w-full focus:outline-0" placeholder="Name" v-model="recurrentName"/>
|
<input class="font-extralight w-full focus:outline-0" placeholder="Name" v-model="recurrentName"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full justify-items-start">
|
<div class="flex flex-col w-full justify-items-start">
|
||||||
<label class="!font-semibold text-2xl text-gray-600 !pl-2">Recurrent date</label>
|
<label class="!font-semibold text-gray-600 !pl-2">Recurrent date</label>
|
||||||
<div class="card !justify-start !items-start !pl-2">
|
<div class="card !justify-start !items-start !pl-2">
|
||||||
<div class="!grid !grid-cols-7 gap-2">
|
<div class="!grid !grid-cols-7 gap-2">
|
||||||
<div v-for="i in 31" class="!w-12 !h-12 !items-center !justify-items-center !justify-center rounded-full "
|
<div v-for="i in 31" class="!w-12 !h-12 !items-center !justify-items-center !justify-center rounded-full "
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ onMounted(fetchData);
|
|||||||
<span class="font-bold">Selected space: {{spaceName}}</span>
|
<span class="font-bold">Selected space: {{spaceName}}</span>
|
||||||
<div v-for="space in spaces" :key="space.id" class="w-full h-full " @click="selectSpace(space)" >
|
<div v-for="space in spaces" :key="space.id" class="w-full h-full " @click="selectSpace(space)" >
|
||||||
<div class="flex w-full flex-col justify-start rounded-2xl p-2 bg-gray-50 gap-2" :class="spaceId === space.id ? '!bg-green-50' : 'bg-gray-50'">
|
<div class="flex w-full flex-col justify-start rounded-2xl p-2 bg-gray-50 gap-2" :class="spaceId === space.id ? '!bg-green-50' : 'bg-gray-50'">
|
||||||
<span class="text-2xl font-medium ">{{ space.name }}</span>
|
<span class=" font-medium ">{{ space.name }}</span>
|
||||||
<div class="w-10 h-10 rounded-full bg-green-200 flex items-center justify-center ">
|
<div class="w-10 h-10 rounded-full bg-green-200 flex items-center justify-center ">
|
||||||
<span class="font-bold ">{{
|
<span class="font-bold ">{{
|
||||||
space.owner.firstName.substring(0, 1).toUpperCase()
|
space.owner.firstName.substring(0, 1).toUpperCase()
|
||||||
|
|||||||
Reference in New Issue
Block a user