-
Notifications
You must be signed in to change notification settings - Fork 0
chore: navigation rework #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |||||||||||
| <div | ||||||||||||
| class="relative py-1 w-full rounded-2xl flex flex-row items-center justify-center" | ||||||||||||
| :class="[ | ||||||||||||
| isThisRoute && 'tg-bg-button tg-text-button motion-translate-y-in', | ||||||||||||
| (isThisRoute || isThisName) && 'tg-bg-button tg-text-button motion-translate-y-in', | ||||||||||||
| ]" | ||||||||||||
| > | ||||||||||||
| <UIcon | ||||||||||||
|
|
@@ -15,20 +15,29 @@ | |||||||||||
| class="size-6 motion-preset-shake" | ||||||||||||
| /> | ||||||||||||
| <UIcon | ||||||||||||
| v-else-if="isFlowInnerPage && canReturnToMain && route.name === 'flow'" | ||||||||||||
| v-else-if="router.currentRoute.value.meta.canReturn && isThisName" | ||||||||||||
| name="i-lucide-undo-2" | ||||||||||||
| class="size-6 motion-preset-shake" | ||||||||||||
| /> | ||||||||||||
| <UIcon | ||||||||||||
| <UChip | ||||||||||||
| v-else | ||||||||||||
| :name="route.icon" | ||||||||||||
| class="size-6 motion-preset-shake" | ||||||||||||
| /> | ||||||||||||
| size="3xl" | ||||||||||||
| :show="!!route.badge" | ||||||||||||
| :text="route.badge" | ||||||||||||
| :ui="{ | ||||||||||||
| base: '-right-1 px-1.5 py-2 ring-2 tg-text-button font-bold motion-translate-y-loop-25 motion-duration-3500', | ||||||||||||
| }" | ||||||||||||
| > | ||||||||||||
| <UIcon | ||||||||||||
| :name="route.icon" | ||||||||||||
| class="size-6 motion-preset-shake" | ||||||||||||
| /> | ||||||||||||
| </UChip> | ||||||||||||
| </div> | ||||||||||||
| <p | ||||||||||||
| class="text-xs font-medium" | ||||||||||||
| :class="[ | ||||||||||||
| isThisRoute && 'tg-text', | ||||||||||||
| (isThisRoute || isThisName) && 'tg-text', | ||||||||||||
| ]" | ||||||||||||
| > | ||||||||||||
| {{ route.title }} | ||||||||||||
|
|
@@ -40,10 +49,11 @@ | |||||||||||
| const { route } = defineProps<{ route: NavigationRoute }>() | ||||||||||||
|
|
||||||||||||
| const { vibrate } = useFeedback() | ||||||||||||
| const { canScrollToTop, isMainPage, isFlowInnerPage, canReturnToMain } = useNavigation() | ||||||||||||
| const { canScrollToTop, isMainPage } = useNavigation() | ||||||||||||
| const router = useRouter() | ||||||||||||
|
|
||||||||||||
| const isThisRoute = computed(() => route.exact ? router.currentRoute.value.path === route.path : router.currentRoute.value.path.startsWith(route.path)) | ||||||||||||
| const isThisName = computed(() => route.names.includes(router.currentRoute.value.name)) | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type-safety fix: route.name can be RouteRecordName | null. Guard before includes to avoid TS errors and edge cases. -const isThisName = computed(() => route.names.includes(router.currentRoute.value.name))
+const isThisName = computed(() => {
+ const n = router.currentRoute.value.name
+ return typeof n === 'string' && route.names.includes(n)
+})📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
| function handleScrollToTop() { | ||||||||||||
| vibrate() | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,25 +3,28 @@ function _useNavigation() { | |||||||||||||||||||||||||||||||
| const { t } = useI18n() | ||||||||||||||||||||||||||||||||
| const { y } = useWindowScroll() | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const taskStore = useTaskStore() | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const mainRoutes = computed<NavigationRoute[]>(() => [ | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| path: '/', | ||||||||||||||||||||||||||||||||
| name: 'flow', | ||||||||||||||||||||||||||||||||
| names: ['index', 'flow-itemId'], | ||||||||||||||||||||||||||||||||
| title: t('app.flow'), | ||||||||||||||||||||||||||||||||
| icon: 'i-lucide-waves', | ||||||||||||||||||||||||||||||||
| exact: true, | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| path: '/epic', | ||||||||||||||||||||||||||||||||
| name: 'epic', | ||||||||||||||||||||||||||||||||
| names: ['epic', 'epic-epicId'], | ||||||||||||||||||||||||||||||||
| title: t('app.epics'), | ||||||||||||||||||||||||||||||||
| icon: 'i-lucide-crown', | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| path: '/tasks', | ||||||||||||||||||||||||||||||||
| name: 'tasks', | ||||||||||||||||||||||||||||||||
| names: ['tasks'], | ||||||||||||||||||||||||||||||||
| title: t('app.my-tasks'), | ||||||||||||||||||||||||||||||||
| icon: 'i-lucide-layout-dashboard', | ||||||||||||||||||||||||||||||||
| badge: taskStore.myTodayTasks.length.toString(), | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
|
Comment on lines
23
to
28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid showing a “0” badge for /tasks Currently badge is always a string; '0' is truthy and will render a chip. Return undefined when count is zero. {
path: '/tasks',
names: ['tasks'],
title: t('app.my-tasks'),
icon: 'i-lucide-layout-dashboard',
- badge: taskStore.myTodayTasks.length.toString(),
+ badge: taskStore.myTodayTasks.length > 0
+ ? String(taskStore.myTodayTasks.length)
+ : undefined,
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -30,15 +33,9 @@ function _useNavigation() { | |||||||||||||||||||||||||||||||
| const isMainPage = computed(() => router.currentRoute.value.path === '/') | ||||||||||||||||||||||||||||||||
| const canScrollToTop = computed(() => y.value > 650) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const isFlowInnerPage = computed(() => router.currentRoute.value.path.startsWith('/flow')) | ||||||||||||||||||||||||||||||||
| const canReturnToMain = computed(() => isFlowInnerPage.value && router.currentRoute.value.path !== '/') | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||
| isNavigationShown, | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| isFlowInnerPage, | ||||||||||||||||||||||||||||||||
| canReturnToMain, | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| isMainPage, | ||||||||||||||||||||||||||||||||
| canScrollToTop, | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { Chat, ChatMember, Task, TaskList, User } from '@roll-stack/database' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getLocalTimeZone, isToday, parseDate } from '@internationalized/date' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { initDataRaw as _initDataRaw, useSignal } from '@telegram-apps/sdk-vue' | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| type ChatWithData = Chat & { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,6 +16,15 @@ export const useTaskStore = defineStore('task', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||
| const isTodayOnly = ref(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const isInitialized = ref(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const userStore = useUserStore() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const myLists = computed(() => | ||||||||||||||||||||||||||||||||||||||||||||||||||
| lists.value.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| (taskList) => taskList.chat?.members.some((member) => member.userId === userStore.id), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ).filter((taskList) => isTodayOnly.value ? taskList.tasks.filter((task) => !task.completedAt && task.date && isToday(parseDate(task.date), getLocalTimeZone())).length : true), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const myTodayTasks = computed(() => myLists.value.flatMap((taskList) => taskList.tasks.filter((task) => !task.completedAt && task.date && isToday(parseDate(task.date), getLocalTimeZone())))) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Predicate returns number; fix boolean logic and avoid repeated TZ lookups The second filter relies on a numeric length for truthiness, which is a TS type mismatch. Also, you call getLocalTimeZone repeatedly inside tight loops. const userStore = useUserStore()
- const myLists = computed(() =>
- lists.value.filter(
- (taskList) => taskList.chat?.members.some((member) => member.userId === userStore.id),
- ).filter((taskList) => isTodayOnly.value ? taskList.tasks.filter((task) => !task.completedAt && task.date && isToday(parseDate(task.date), getLocalTimeZone())).length : true),
- )
- const myTodayTasks = computed(() => myLists.value.flatMap((taskList) => taskList.tasks.filter((task) => !task.completedAt && task.date && isToday(parseDate(task.date), getLocalTimeZone()))))
+ const tz = getLocalTimeZone()
+ const isTaskForToday = (task: Task) =>
+ !task.completedAt && task.date && isToday(parseDate(task.date), tz)
+
+ const myLists = computed(() =>
+ lists.value
+ .filter((taskList) =>
+ taskList.chat?.members.some((member) => member.userId === userStore.id),
+ )
+ .filter((taskList) => !isTodayOnly.value || taskList.tasks.some(isTaskForToday)),
+ )
+ const myTodayTasks = computed(() =>
+ myLists.value.flatMap((taskList) => taskList.tasks.filter(isTaskForToday)),
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| const initDataRaw = useSignal(_initDataRaw) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| async function update() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -82,6 +92,9 @@ export const useTaskStore = defineStore('task', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||
| isTodayOnly, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| isInitialized, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| myLists, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| myTodayTasks, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| update, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| setAsFocused, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| setAsUnfocused, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Don’t show chip when badge is '0'
Guard with a numeric check to avoid rendering a “0” badge.
📝 Committable suggestion
🤖 Prompt for AI Agents