mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 06:56:04 +02:00
Upgrade to Vue3 and Element Plus (#1374)
* Update Vue devtools
* upgrade vue pinia element-plus vue-i18n, add vite
* fix: i18n
* global components
* change v-deep
* upgrade vue-lazyload
* data table
* update enlint and safe-dialog
* package.json and vite.config.js
* el-icon
* el-message
* vue 2 -> vue3 migration changes
* $pinia
* dialog
* el-popover slot
* lint
* chore
* slot
* scss
* remote state access
* misc
* jsconfig
* el-button size mini -> small
* :model-value
* ElMessageBox
* datatable
* remove v-lazyload
* template #dropdown
* mini -> small
* css
* byebye hideTooltips
* use sass-embedded
* Update SQLite, remove unneeded libraries
* Fix shift remove local avatar favorites
* Electron arm64
* arm64 support
* bye pug
* f-word vite hah
* misc
* remove safe dialog component
* Add self invite to launch dialog
* Fix errors
* Icons 1
* improve localfavorite loading performance
* improve favorites world item performance
* dialog visibility changes for Element Plus
* clear element plus error
* import performance
* revert App.vue hah
* hah
* Revert "Add self invite to launch dialog"
This reverts commit 4801cfad58.
* Toggle self invite/open in-game
* Self invite on launch dialog
* el-button icon
* el-icon
* fix user dialog tab switching logic
* fix PlayerList
* Formatting changes
* More icons
* Fix friend log table
* loading margin
* fix markdown
* fix world dialog tab switching issue
* Fixes and formatting
* fix: global i18n.t export
* fix favorites world tab not working
* Create instance, displayName
* Remove group members sort by userId
* Fix loading dialog tabs on swtich
* Star
* charts console.warn
* wip: fix charts
* wip: fix charts
* wip: charts composables
* fix favorite item tooltip warning
* Fixes and formatting
* Clean up image dialogs
* Remove unused method
* Fix platform/size border
* Fix platform/size border
* $vr
* fix friendExportDialogVisible binding
* ElMessageBox and Settings
* Login formatting
* Rename VR overlay query
* Fix image popover and userdialog badges
* Formatting
* Big buttons
* Fixes, update Cef
* Fix gameLog table nav buttons jumping around while using nav buttons
* Fix z-index
* vr overlay
* vite input add theme
* defineAsyncComponent
* ISO 639-1
* fix i18n
* clean t
* Formatting, fix calendar, rotate arrows
* Show user status when user is offline
* Fix VR overlay
* fix theme and clean up
* split InstanceActivity
* tweak
* Fix VR overlay formatting
* fix scss var
* AppDebug hahahaha
* Years
* remove reactive
* improve perf
* state hah…
* fix user rendering poblems when user object is not yet loaded
* improve perf
* Update avatar/world image uploader, licenses, remove previous images dialog (old images are now deleted)
* improve perf 1
* Suppress stray errors
* fix traveling location display issue
* Fix empty instance creator
* improve friend list refresh performance
* fix main charts
* fix chart
* Fix darkmode
* Fix avatar dialog tags
---------
Co-authored-by: pa <maplenagisa@gmail.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
export function useActivityDataFilter(activityDetailData, isDetailVisible, isSoloInstanceVisible, isNoFriendInstanceVisible) {
|
||||
const filteredActivityDetailData = computed(() => {
|
||||
if (!isDetailVisible.value) {
|
||||
return [];
|
||||
}
|
||||
let result = [...activityDetailData.value];
|
||||
if (!isSoloInstanceVisible.value) {
|
||||
result = result.filter((arr) => arr.length > 1);
|
||||
}
|
||||
if (!isNoFriendInstanceVisible.value) {
|
||||
result = result.filter((arr) => {
|
||||
// solo instance
|
||||
if (arr.length === 1) {
|
||||
return true;
|
||||
}
|
||||
return arr.some((item) => item.isFriend);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return {
|
||||
filteredActivityDetailData
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
export function useActivityDataProcessor(
|
||||
activityData,
|
||||
activityDetailData,
|
||||
isDetailVisible,
|
||||
isSoloInstanceVisible,
|
||||
isNoFriendInstanceVisible
|
||||
) {
|
||||
const totalOnlineTime = computed(() => {
|
||||
return activityData.value?.reduce((acc, item) => acc + item.time, 0);
|
||||
});
|
||||
|
||||
const filteredActivityDetailData = computed(() => {
|
||||
if (!isDetailVisible.value) {
|
||||
return [];
|
||||
}
|
||||
let result = [...activityDetailData.value];
|
||||
if (!isSoloInstanceVisible.value) {
|
||||
result = result.filter((arr) => arr.length > 1);
|
||||
}
|
||||
if (!isNoFriendInstanceVisible.value) {
|
||||
result = result.filter((arr) => {
|
||||
// solo instance
|
||||
if (arr.length === 1) {
|
||||
return true;
|
||||
}
|
||||
return arr.some((item) => item.isFriend);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return {
|
||||
totalOnlineTime,
|
||||
filteredActivityDetailData
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
export function useActivityStats(activityData) {
|
||||
const totalOnlineTime = computed(() => {
|
||||
return activityData.value?.reduce((acc, item) => acc + item.time, 0);
|
||||
});
|
||||
|
||||
return {
|
||||
totalOnlineTime
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
export function isDetailDataFiltered(
|
||||
detailData,
|
||||
isSoloInstanceVisible,
|
||||
isNoFriendInstanceVisible
|
||||
) {
|
||||
if (!detailData) return false;
|
||||
|
||||
if (!isSoloInstanceVisible && detailData.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
!isNoFriendInstanceVisible &&
|
||||
detailData.length > 1 &&
|
||||
!detailData.some((item) => item.isFriend)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function findMatchingDetailData(
|
||||
activityItem,
|
||||
activityDetailData,
|
||||
currentUser
|
||||
) {
|
||||
if (!activityItem || !currentUser) return null;
|
||||
|
||||
return activityDetailData.find((arr) => {
|
||||
const sameLocation = arr[0]?.location === activityItem.location;
|
||||
const sameJoinTime = arr
|
||||
.find((item) => item.user_id === currentUser.id)
|
||||
?.joinTime.isSame(activityItem.joinTime);
|
||||
return sameLocation && sameJoinTime;
|
||||
});
|
||||
}
|
||||
|
||||
export function generateYAxisLabel(worldName, isFiltered, maxLength = 20) {
|
||||
const truncatedName =
|
||||
worldName.length > maxLength
|
||||
? `${worldName.slice(0, maxLength)}...`
|
||||
: worldName;
|
||||
return isFiltered
|
||||
? `{filtered|${truncatedName}}`
|
||||
: `{normal|${truncatedName}}`;
|
||||
}
|
||||
|
||||
export function formatWorldName(worldName, maxLength = 20) {
|
||||
return worldName.length > maxLength
|
||||
? `${worldName.slice(0, maxLength)}...`
|
||||
: worldName;
|
||||
}
|
||||
|
||||
export function useChartHelpers() {
|
||||
return {
|
||||
isDetailDataFiltered,
|
||||
findMatchingDetailData,
|
||||
generateYAxisLabel,
|
||||
formatWorldName
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export function useDateNavigation(allDateOfActivity, reloadData) {
|
||||
const selectedDate = ref(dayjs().toDate());
|
||||
|
||||
const allDateOfActivityArray = computed(() => {
|
||||
return allDateOfActivity.value
|
||||
? Array.from(allDateOfActivity.value)
|
||||
.map((item) => dayjs(item))
|
||||
.sort((a, b) => b.valueOf() - a.valueOf())
|
||||
: [];
|
||||
});
|
||||
|
||||
const isNextDayBtnDisabled = computed(() => {
|
||||
return dayjs(selectedDate.value).isSameOrAfter(
|
||||
allDateOfActivityArray.value[0],
|
||||
'day'
|
||||
);
|
||||
});
|
||||
|
||||
const isPrevDayBtnDisabled = computed(() => {
|
||||
return dayjs(selectedDate.value).isSame(
|
||||
allDateOfActivityArray.value[
|
||||
allDateOfActivityArray.value.length - 1
|
||||
],
|
||||
'day'
|
||||
);
|
||||
});
|
||||
|
||||
function changeSelectedDateFromBtn(isNext = false) {
|
||||
if (
|
||||
!allDateOfActivityArray.value ||
|
||||
allDateOfActivityArray.value.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = allDateOfActivityArray.value.findIndex((date) =>
|
||||
date.isSame(selectedDate.value, 'day')
|
||||
);
|
||||
if (idx !== -1) {
|
||||
const newIdx = isNext ? idx - 1 : idx + 1;
|
||||
|
||||
if (newIdx >= 0 && newIdx < allDateOfActivityArray.value.length) {
|
||||
selectedDate.value =
|
||||
allDateOfActivityArray.value[newIdx].toDate();
|
||||
reloadData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
selectedDate.value = isNext
|
||||
? allDateOfActivityArray.value[0].toDate()
|
||||
: allDateOfActivityArray.value[
|
||||
allDateOfActivityArray.value.length - 1
|
||||
].toDate();
|
||||
reloadData();
|
||||
}
|
||||
|
||||
function getDatePickerDisabledDate(time) {
|
||||
if (
|
||||
time > Date.now() ||
|
||||
allDateOfActivityArray.value[
|
||||
allDateOfActivityArray.value.length - 1
|
||||
]
|
||||
?.add(-1, 'day')
|
||||
.isAfter(time, 'day') ||
|
||||
!allDateOfActivity.value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return !allDateOfActivity.value.has(dayjs(time).format('YYYY-MM-DD'));
|
||||
}
|
||||
|
||||
return {
|
||||
selectedDate,
|
||||
isNextDayBtnDisabled,
|
||||
isPrevDayBtnDisabled,
|
||||
changeSelectedDateFromBtn,
|
||||
getDatePickerDisabledDate
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
import { ref, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { database } from '../../../service/database';
|
||||
import { getWorldName } from '../../../shared/utils';
|
||||
|
||||
export function useInstanceActivityData() {
|
||||
const activityData = ref([]);
|
||||
const activityDetailData = ref([]);
|
||||
const allDateOfActivity = ref(new Set());
|
||||
const worldNameArray = ref([]);
|
||||
|
||||
async function getAllDateOfActivity() {
|
||||
const utcDateStrings =
|
||||
(await database.getDateOfInstanceActivity()) || [];
|
||||
const uniqueDates = new Set();
|
||||
|
||||
for (const utcString of utcDateStrings) {
|
||||
const formattedDate = dayjs
|
||||
.utc(utcString)
|
||||
.tz()
|
||||
.format('YYYY-MM-DD');
|
||||
uniqueDates.add(formattedDate);
|
||||
}
|
||||
|
||||
allDateOfActivity.value = uniqueDates;
|
||||
}
|
||||
|
||||
async function getWorldNameData() {
|
||||
worldNameArray.value = await Promise.all(
|
||||
activityData.value.map(async (item) => {
|
||||
try {
|
||||
return await getWorldName(item.location);
|
||||
} catch {
|
||||
console.error(
|
||||
'getWorldName failed location',
|
||||
item.location
|
||||
);
|
||||
return 'Unknown world';
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function getActivityData(
|
||||
selectedDate,
|
||||
currentUser,
|
||||
friends,
|
||||
localFavoriteFriends,
|
||||
onActivityDetailReady
|
||||
) {
|
||||
const localStartDate = dayjs
|
||||
.tz(selectedDate.value)
|
||||
.startOf('day')
|
||||
.toISOString();
|
||||
const localEndDate = dayjs
|
||||
.tz(selectedDate.value)
|
||||
.endOf('day')
|
||||
.toISOString();
|
||||
const dbData = await database.getInstanceActivity(
|
||||
localStartDate,
|
||||
localEndDate
|
||||
);
|
||||
|
||||
const transformData = (item) => ({
|
||||
...item,
|
||||
joinTime: dayjs(item.created_at).subtract(item.time, 'millisecond'),
|
||||
leaveTime: dayjs(item.created_at),
|
||||
time: item.time < 0 ? 0 : item.time,
|
||||
isFriend:
|
||||
item.user_id === currentUser.value.id
|
||||
? null
|
||||
: friends.value.has(item.user_id),
|
||||
isFavorite:
|
||||
item.user_id === currentUser.value.id
|
||||
? null
|
||||
: localFavoriteFriends.value.has(item.user_id)
|
||||
});
|
||||
|
||||
activityData.value = dbData.currentUserData.map(transformData);
|
||||
|
||||
const transformAndSort = (arr) => {
|
||||
return arr.map(transformData).sort((a, b) => {
|
||||
const timeDiff = Math.abs(
|
||||
a.joinTime.diff(b.joinTime, 'second')
|
||||
);
|
||||
// recording delay, under 3s is considered the same time entry, beautify the chart
|
||||
return timeDiff < 3
|
||||
? a.leaveTime - b.leaveTime
|
||||
: a.joinTime - b.joinTime;
|
||||
});
|
||||
};
|
||||
|
||||
const filterByLocation = (innerArray, locationSet) => {
|
||||
return innerArray.every((innerObject) =>
|
||||
locationSet.has(innerObject.location)
|
||||
);
|
||||
};
|
||||
const locationSet = new Set(
|
||||
activityData.value.map((item) => item.location)
|
||||
);
|
||||
|
||||
const preSplitActivityDetailData = Array.from(
|
||||
dbData.detailData.values()
|
||||
)
|
||||
.map(transformAndSort)
|
||||
.filter((innerArray) => filterByLocation(innerArray, locationSet));
|
||||
|
||||
activityDetailData.value = handleSplitActivityDetailData(
|
||||
preSplitActivityDetailData,
|
||||
currentUser.value.id
|
||||
);
|
||||
|
||||
if (activityDetailData.value.length && onActivityDetailReady) {
|
||||
nextTick(() => {
|
||||
onActivityDetailReady();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleSplitActivityDetailData(activityDetailData, currentUserId) {
|
||||
function countTargetIdOccurrences(innerArray, targetId) {
|
||||
let count = 0;
|
||||
for (const obj of innerArray) {
|
||||
if (obj.user_id === targetId) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function areIntervalsOverlapping(objA, objB) {
|
||||
const isObj1EndTimeBeforeObj2StartTime = objA.leaveTime.isBefore(
|
||||
objB.joinTime,
|
||||
'second'
|
||||
);
|
||||
const isObj2EndTimeBeforeObj1StartTime = objB.leaveTime.isBefore(
|
||||
objA.joinTime,
|
||||
'second'
|
||||
);
|
||||
return !(
|
||||
isObj1EndTimeBeforeObj2StartTime ||
|
||||
isObj2EndTimeBeforeObj1StartTime
|
||||
);
|
||||
}
|
||||
|
||||
function buildOverlapGraph(innerArray) {
|
||||
const numObjects = innerArray.length;
|
||||
const adjacencyList = Array.from({ length: numObjects }, () => []);
|
||||
|
||||
for (let i = 0; i < numObjects; i++) {
|
||||
for (let j = i + 1; j < numObjects; j++) {
|
||||
if (areIntervalsOverlapping(innerArray[i], innerArray[j])) {
|
||||
adjacencyList[i].push(j);
|
||||
adjacencyList[j].push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return adjacencyList;
|
||||
}
|
||||
|
||||
function depthFirstSearch(nodeIndex, visited, graph, component) {
|
||||
visited[nodeIndex] = true;
|
||||
component.push(nodeIndex);
|
||||
for (const neighborIndex of graph[nodeIndex]) {
|
||||
if (!visited[neighborIndex]) {
|
||||
depthFirstSearch(neighborIndex, visited, graph, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findConnectedComponents(graph, numNodes) {
|
||||
const visited = new Array(numNodes).fill(false);
|
||||
const components = [];
|
||||
|
||||
for (let i = 0; i < numNodes; i++) {
|
||||
if (!visited[i]) {
|
||||
const component = [];
|
||||
depthFirstSearch(i, visited, graph, component);
|
||||
components.push(component);
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
function processOuterArrayWithTargetId(outerArray, targetId) {
|
||||
let i = 0;
|
||||
while (i < outerArray.length) {
|
||||
let currentInnerArray = outerArray[i];
|
||||
let targetIdCount = countTargetIdOccurrences(
|
||||
currentInnerArray,
|
||||
targetId
|
||||
);
|
||||
if (targetIdCount > 1) {
|
||||
let graph = buildOverlapGraph(currentInnerArray);
|
||||
let connectedComponents = findConnectedComponents(
|
||||
graph,
|
||||
currentInnerArray.length
|
||||
);
|
||||
let newInnerArrays = connectedComponents.map(
|
||||
(componentIndices) => {
|
||||
return componentIndices.map(
|
||||
(index) => currentInnerArray[index]
|
||||
);
|
||||
}
|
||||
);
|
||||
outerArray.splice(i, 1, ...newInnerArrays);
|
||||
i += newInnerArrays.length;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return outerArray.sort((a, b) => a[0].joinTime - b[0].joinTime);
|
||||
}
|
||||
|
||||
return processOuterArrayWithTargetId(activityDetailData, currentUserId);
|
||||
}
|
||||
|
||||
return {
|
||||
activityData,
|
||||
activityDetailData,
|
||||
allDateOfActivity,
|
||||
worldNameArray,
|
||||
|
||||
getAllDateOfActivity,
|
||||
getWorldNameData,
|
||||
getActivityData
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { ref, nextTick } from 'vue';
|
||||
import configRepository from '../../../service/config';
|
||||
|
||||
export function useInstanceActivitySettings() {
|
||||
const barWidth = ref(25);
|
||||
const isDetailVisible = ref(true);
|
||||
const isSoloInstanceVisible = ref(true);
|
||||
const isNoFriendInstanceVisible = ref(true);
|
||||
|
||||
async function initializeSettings() {
|
||||
try {
|
||||
const [
|
||||
barWidthValue,
|
||||
isDetailVisibleValue,
|
||||
isSoloInstanceVisibleValue,
|
||||
isNoFriendInstanceVisibleValue
|
||||
] = await Promise.all([
|
||||
configRepository.getInt('VRCX_InstanceActivityBarWidth', 25),
|
||||
configRepository.getBool(
|
||||
'VRCX_InstanceActivityDetailVisible',
|
||||
true
|
||||
),
|
||||
configRepository.getBool(
|
||||
'VRCX_InstanceActivitySoloInstanceVisible',
|
||||
true
|
||||
),
|
||||
configRepository.getBool(
|
||||
'VRCX_InstanceActivityNoFriendInstanceVisible',
|
||||
true
|
||||
)
|
||||
]);
|
||||
|
||||
barWidth.value = barWidthValue;
|
||||
isDetailVisible.value = isDetailVisibleValue;
|
||||
isSoloInstanceVisible.value = isSoloInstanceVisibleValue;
|
||||
isNoFriendInstanceVisible.value = isNoFriendInstanceVisibleValue;
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize settings:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function changeBarWidth(value, onSettingsChange) {
|
||||
barWidth.value = value;
|
||||
configRepository
|
||||
.setInt('VRCX_InstanceActivityBarWidth', value)
|
||||
.finally(() => {
|
||||
if (onSettingsChange) onSettingsChange();
|
||||
});
|
||||
}
|
||||
|
||||
function changeIsDetailInstanceVisible(value, onSettingsChange) {
|
||||
isDetailVisible.value = value;
|
||||
configRepository
|
||||
.setBool('VRCX_InstanceActivityDetailVisible', value)
|
||||
.finally(() => {
|
||||
if (onSettingsChange) onSettingsChange();
|
||||
});
|
||||
}
|
||||
|
||||
function changeIsSoloInstanceVisible(value, onSettingsChange) {
|
||||
isSoloInstanceVisible.value = value;
|
||||
configRepository
|
||||
.setBool('VRCX_InstanceActivitySoloInstanceVisible', value)
|
||||
.finally(() => {
|
||||
if (onSettingsChange) onSettingsChange();
|
||||
});
|
||||
}
|
||||
|
||||
function changeIsNoFriendInstanceVisible(value, onSettingsChange) {
|
||||
isNoFriendInstanceVisible.value = value;
|
||||
configRepository
|
||||
.setBool('VRCX_InstanceActivityNoFriendInstanceVisible', value)
|
||||
.finally(() => {
|
||||
if (onSettingsChange) onSettingsChange();
|
||||
});
|
||||
}
|
||||
|
||||
function handleChangeSettings(activityDetailChartRef) {
|
||||
nextTick(() => {
|
||||
if (activityDetailChartRef.value) {
|
||||
activityDetailChartRef.value.forEach((child) => {
|
||||
requestAnimationFrame(() => {
|
||||
if (child.echartsInstance) {
|
||||
child.initEcharts();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
barWidth,
|
||||
isDetailVisible,
|
||||
isSoloInstanceVisible,
|
||||
isNoFriendInstanceVisible,
|
||||
|
||||
initializeSettings,
|
||||
changeBarWidth,
|
||||
changeIsDetailInstanceVisible,
|
||||
changeIsSoloInstanceVisible,
|
||||
changeIsNoFriendInstanceVisible,
|
||||
handleChangeSettings
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export function useIntersectionObserver() {
|
||||
const intersectionObservers = ref([]);
|
||||
|
||||
// intersection observer - start
|
||||
function clearIntersectionObservers() {
|
||||
intersectionObservers.value.forEach((observer) => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
intersectionObservers.value = [];
|
||||
}
|
||||
|
||||
function handleIntersectionObserver(activityDetailChartRef) {
|
||||
clearIntersectionObservers();
|
||||
|
||||
activityDetailChartRef.value?.forEach((child, index) => {
|
||||
const observer = new IntersectionObserver((entries) =>
|
||||
handleIntersection(index, entries, activityDetailChartRef)
|
||||
);
|
||||
observer.observe(child.$el);
|
||||
intersectionObservers.value[index] = observer;
|
||||
});
|
||||
}
|
||||
function handleIntersection(index, entries, activityDetailChartRef) {
|
||||
if (!entries) {
|
||||
console.error('handleIntersection failed');
|
||||
return;
|
||||
}
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting && activityDetailChartRef.value[index]) {
|
||||
activityDetailChartRef.value[index].initEcharts();
|
||||
intersectionObservers.value[index].unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}
|
||||
// intersection observer - end
|
||||
|
||||
return {
|
||||
handleIntersectionObserver
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user