improve performance for mobile transaction list page
This commit is contained in:
@@ -100,6 +100,34 @@ export function getFontSizePreviewClassName(type: number): string {
|
|||||||
return FONT_SIZE_PREVIEW_CLASSNAME_PREFIX + FontSize.Default.className;
|
return FONT_SIZE_PREVIEW_CLASSNAME_PREFIX + FontSize.Default.className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getElementActualHeights(selector: string): Record<string, number> {
|
||||||
|
const elements = f7.$(selector);
|
||||||
|
const heights: Record<string, number> = {};
|
||||||
|
|
||||||
|
if (!elements || !elements.length) {
|
||||||
|
return heights;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
const el = elements[i];
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
heights[el.id] = rect.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return heights;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElementBoundingRect(selector: string): DOMRect | null {
|
||||||
|
const elements = f7.$(selector);
|
||||||
|
|
||||||
|
if (!elements || !elements.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = elements[0];
|
||||||
|
return el.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
export function scrollToSelectedItem(parentEl: Framework7Dom, containerSelector: string, selectedItemSelector: string): void {
|
export function scrollToSelectedItem(parentEl: Framework7Dom, containerSelector: string, selectedItemSelector: string): void {
|
||||||
if (!parentEl || !parentEl.length) {
|
if (!parentEl || !parentEl.length) {
|
||||||
return;
|
return;
|
||||||
@@ -158,6 +186,14 @@ export function scrollSheetToTop(sheetElement: HTMLElement | undefined, windowNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function onInfiniteScrolling(callback: (e: Event) => void): void {
|
||||||
|
f7.$('.infinite-scroll-content').on('scroll', (e: Event) => {
|
||||||
|
callback(e);
|
||||||
|
}, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useI18nUIComponents() {
|
export function useI18nUIComponents() {
|
||||||
const { tt, te } = useI18n();
|
const { tt, te } = useI18n();
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
<f7-accordion-item :opened="transactionMonthList.opened"
|
<f7-accordion-item :opened="transactionMonthList.opened"
|
||||||
@accordion:open="collapseTransactionMonthList(transactionMonthList, false)"
|
@accordion:open="collapseTransactionMonthList(transactionMonthList, false)"
|
||||||
@accordion:close="collapseTransactionMonthList(transactionMonthList, true)">
|
@accordion:close="collapseTransactionMonthList(transactionMonthList, true)">
|
||||||
<f7-block-title v-if="pageType === TransactionListPageType.List.type">
|
<f7-block-title :id="getTransactionMonthTitleDomId(transactionMonthList.yearMonth)" v-if="pageType === TransactionListPageType.List.type">
|
||||||
<f7-accordion-toggle>
|
<f7-accordion-toggle>
|
||||||
<f7-list strong inset dividers media-list
|
<f7-list strong inset dividers media-list
|
||||||
class="transaction-amount-list combination-list-header"
|
class="transaction-amount-list combination-list-header"
|
||||||
@@ -202,8 +202,12 @@
|
|||||||
</f7-list>
|
</f7-list>
|
||||||
</f7-accordion-toggle>
|
</f7-accordion-toggle>
|
||||||
</f7-block-title>
|
</f7-block-title>
|
||||||
<f7-accordion-content :style="{ height: transactionMonthList.opened ? 'auto' : '' }">
|
<f7-accordion-content :style="{ height: getTransactionMonthListHeight(transactionMonthList) }">
|
||||||
<f7-list strong inset dividers media-list accordion-list class="transaction-info-list combination-list-content">
|
<f7-list strong inset dividers media-list accordion-list
|
||||||
|
class="transaction-info-list transaction-month-list combination-list-content"
|
||||||
|
:id="getTransactionMonthListDomId(transactionMonthList.yearMonth)"
|
||||||
|
v-if="!isTransactionMonthListInvisible(transactionMonthList)"
|
||||||
|
>
|
||||||
<f7-list-item swipeout chevron-center
|
<f7-list-item swipeout chevron-center
|
||||||
class="transaction-info"
|
class="transaction-info"
|
||||||
:id="getTransactionDomId(transaction)"
|
:id="getTransactionDomId(transaction)"
|
||||||
@@ -579,11 +583,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, nextTick, onMounted, onUnmounted } from 'vue';
|
||||||
import type { Router } from 'framework7/types';
|
import type { Router } from 'framework7/types';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { type Framework7Dom, useI18nUIComponents, showLoading, hideLoading, onSwipeoutDeleted, scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
import {
|
||||||
|
type Framework7Dom,
|
||||||
|
useI18nUIComponents,
|
||||||
|
showLoading,
|
||||||
|
hideLoading,
|
||||||
|
onSwipeoutDeleted,
|
||||||
|
getElementActualHeights,
|
||||||
|
getElementBoundingRect,
|
||||||
|
scrollToSelectedItem,
|
||||||
|
onInfiniteScrolling
|
||||||
|
} from '@/lib/ui/mobile.ts';
|
||||||
import { TransactionListPageType, useTransactionListPageBase } from '@/views/base/transactions/TransactionListPageBase.ts';
|
import { TransactionListPageType, useTransactionListPageBase } from '@/views/base/transactions/TransactionListPageBase.ts';
|
||||||
|
|
||||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||||
@@ -604,6 +618,7 @@ import type { TransactionCategory } from '@/models/transaction_category.ts';
|
|||||||
import type { Transaction } from '@/models/transaction.ts';
|
import type { Transaction } from '@/models/transaction.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
isNumber,
|
||||||
arrangeArrayWithNewStartIndex
|
arrangeArrayWithNewStartIndex
|
||||||
} from '@/lib/common.ts';
|
} from '@/lib/common.ts';
|
||||||
import {
|
import {
|
||||||
@@ -699,12 +714,11 @@ const transactionCategoriesStore = useTransactionCategoriesStore();
|
|||||||
const transactionTagsStore = useTransactionTagsStore();
|
const transactionTagsStore = useTransactionTagsStore();
|
||||||
const transactionsStore = useTransactionsStore();
|
const transactionsStore = useTransactionsStore();
|
||||||
|
|
||||||
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
|
|
||||||
const dayNames = computed<string[]>(() => arrangeArrayWithNewStartIndex(getAllShortWeekdayNames(), firstDayOfWeek.value));
|
|
||||||
|
|
||||||
const loadingError = ref<unknown | null>(null);
|
const loadingError = ref<unknown | null>(null);
|
||||||
const loadingMore = ref<boolean>(false);
|
const loadingMore = ref<boolean>(false);
|
||||||
const transactionToDelete = ref<Transaction | null>(null);
|
const transactionToDelete = ref<Transaction | null>(null);
|
||||||
|
const transactionInvisibleYearMonths = ref<Record<string, boolean>>({});
|
||||||
|
const transactionYearMonthListHeights = ref<Record<string, number>>({});
|
||||||
const showTransactionListPageTypePopover = ref<boolean>(false);
|
const showTransactionListPageTypePopover = ref<boolean>(false);
|
||||||
const showDatePopover = ref<boolean>(false);
|
const showDatePopover = ref<boolean>(false);
|
||||||
const showCategoryPopover = ref<boolean>(false);
|
const showCategoryPopover = ref<boolean>(false);
|
||||||
@@ -714,6 +728,9 @@ const showCustomDateRangeSheet = ref<boolean>(false);
|
|||||||
const showCustomMonthSheet = ref<boolean>(false);
|
const showCustomMonthSheet = ref<boolean>(false);
|
||||||
const showDeleteActionSheet = ref<boolean>(false);
|
const showDeleteActionSheet = ref<boolean>(false);
|
||||||
|
|
||||||
|
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
|
||||||
|
const dayNames = computed<string[]>(() => arrangeArrayWithNewStartIndex(getAllShortWeekdayNames(), firstDayOfWeek.value));
|
||||||
|
|
||||||
const allTransactionTagFilterTypes = computed<TypeAndDisplayName[]>(() => getAllTransactionTagFilterTypes());
|
const allTransactionTagFilterTypes = computed<TypeAndDisplayName[]>(() => getAllTransactionTagFilterTypes());
|
||||||
|
|
||||||
const transactions = computed<TransactionMonthList[]>(() => {
|
const transactions = computed<TransactionMonthList[]>(() => {
|
||||||
@@ -777,10 +794,99 @@ const noTransaction = computed<boolean>(() => {
|
|||||||
|
|
||||||
const hasMoreTransaction = computed<boolean>(() => transactionsStore.hasMoreTransaction);
|
const hasMoreTransaction = computed<boolean>(() => transactionsStore.hasMoreTransaction);
|
||||||
|
|
||||||
|
function getTransactionMonthTitleDomId(yearMonth: string): string {
|
||||||
|
return 'transaction_month_title_' + yearMonth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTransactionMonthListDomId(yearMonth: string): string {
|
||||||
|
return 'transaction_month_list_' + yearMonth;
|
||||||
|
}
|
||||||
|
|
||||||
function getTransactionDomId(transaction: Transaction): string {
|
function getTransactionDomId(transaction: Transaction): string {
|
||||||
return 'transaction_' + transaction.id;
|
return 'transaction_' + transaction.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isTransactionMonthListInvisible(transactionMonthList: TransactionMonthList): boolean {
|
||||||
|
if (!transactionMonthList.opened) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transactionYearMonthListHeights.value[transactionMonthList.yearMonth]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionInvisibleYearMonths.value[transactionMonthList.yearMonth]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTransactionMonthListHeight(transactionMonthList: TransactionMonthList): string {
|
||||||
|
if (!transactionMonthList.opened) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTransactionMonthListInvisible(transactionMonthList)) {
|
||||||
|
return transactionYearMonthListHeights.value[transactionMonthList.yearMonth] + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTransactionMonthListHeights(reset: boolean): Promise<unknown> {
|
||||||
|
return nextTick(() => {
|
||||||
|
if (reset) {
|
||||||
|
transactionInvisibleYearMonths.value = {};
|
||||||
|
transactionYearMonthListHeights.value = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactions.value && transactions.value.length) {
|
||||||
|
const heights: Record<string, number> = getElementActualHeights('.transaction-month-list');
|
||||||
|
|
||||||
|
for (let i = 0; i < transactions.value.length - 1; i++) {
|
||||||
|
const transactionMonthList = transactions.value[i];
|
||||||
|
const yearMonth = transactionMonthList.yearMonth;
|
||||||
|
const domId = getTransactionMonthListDomId(yearMonth);
|
||||||
|
const height = heights[domId];
|
||||||
|
|
||||||
|
if (!transactionYearMonthListHeights.value[yearMonth] && isNumber(height)) {
|
||||||
|
transactionYearMonthListHeights.value[yearMonth] = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTransactionInvisibleYearMonthList(): void {
|
||||||
|
if (!transactions.value || !transactions.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < transactions.value.length - 1; i++) {
|
||||||
|
const transactionMonthList = transactions.value[i];
|
||||||
|
const yearMonth = transactionMonthList.yearMonth;
|
||||||
|
|
||||||
|
const titleDomId = getTransactionMonthTitleDomId(yearMonth);
|
||||||
|
const titleRect = getElementBoundingRect(`#${titleDomId}`);
|
||||||
|
|
||||||
|
if (!titleRect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listHeight = transactionYearMonthListHeights.value[yearMonth] || 0;
|
||||||
|
const listRectTop = titleRect.top + titleRect.height;
|
||||||
|
const listRectBottom = listRectTop + listHeight;
|
||||||
|
const invisible = listRectTop > 2 * window.innerHeight || listRectBottom < -2 * window.innerHeight;
|
||||||
|
|
||||||
|
if (invisible) {
|
||||||
|
transactionInvisibleYearMonths.value[yearMonth] = true;
|
||||||
|
} else {
|
||||||
|
delete transactionInvisibleYearMonths.value[yearMonth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getTransactionDateStyle(transaction: Transaction, previousTransaction: Transaction | null): Record<string, string> {
|
function getTransactionDateStyle(transaction: Transaction, previousTransaction: Transaction | null): Record<string, string> {
|
||||||
if (!previousTransaction || transaction.day !== previousTransaction.day) {
|
if (!previousTransaction || transaction.day !== previousTransaction.day) {
|
||||||
return {};
|
return {};
|
||||||
@@ -847,6 +953,9 @@ function reload(done?: () => void): void {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transactionInvisibleYearMonths.value = {};
|
||||||
|
transactionYearMonthListHeights.value = {};
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
accountsStore.loadAllAccounts({ force: false }),
|
accountsStore.loadAllAccounts({ force: false }),
|
||||||
transactionCategoriesStore.loadAllCategories({ force: false }),
|
transactionCategoriesStore.loadAllCategories({ force: false }),
|
||||||
@@ -878,6 +987,7 @@ function reload(done?: () => void): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
setTransactionMonthListHeights(true);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.processed || done) {
|
if (error.processed || done) {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -912,6 +1022,7 @@ function loadMore(autoExpand: boolean): void {
|
|||||||
defaultCurrency: defaultCurrency.value
|
defaultCurrency: defaultCurrency.value
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
loadingMore.value = false;
|
loadingMore.value = false;
|
||||||
|
setTransactionMonthListHeights(false);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
loadingMore.value = false;
|
loadingMore.value = false;
|
||||||
|
|
||||||
@@ -1301,6 +1412,12 @@ function collapseTransactionMonthList(month: TransactionMonthList, collapse: boo
|
|||||||
month: month,
|
month: month,
|
||||||
collapse: collapse
|
collapse: collapse
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
return setTransactionMonthListHeights(false);
|
||||||
|
}).then(() => {
|
||||||
|
setTransactionInvisibleYearMonthList();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPopoverOpen(event: { $el: Framework7Dom }): void {
|
function onPopoverOpen(event: { $el: Framework7Dom }): void {
|
||||||
@@ -1315,6 +1432,26 @@ function onPageAfterIn(): void {
|
|||||||
routeBackOnError(props.f7router, loadingError);
|
routeBackOnError(props.f7router, loadingError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onResize(): void {
|
||||||
|
setTransactionMonthListHeights(true)
|
||||||
|
.then(() => {
|
||||||
|
setTransactionMonthListHeights(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScroll(): void {
|
||||||
|
setTransactionInvisibleYearMonthList();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
onInfiniteScrolling(onScroll);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
});
|
||||||
|
|
||||||
init();
|
init();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user