diff --git a/src/stores/gameLog.js b/src/stores/gameLog.js index b387b916..2a5518aa 100644 --- a/src/stores/gameLog.js +++ b/src/stores/gameLog.js @@ -58,8 +58,8 @@ export const useGameLogStore = defineStore('GameLog', () => { lastLocationAvatarList: new Map() }); + const gameLogTableData = shallowReactive([]); const gameLogTable = ref({ - data: shallowReactive([]), loading: false, search: '', filter: [], @@ -88,7 +88,7 @@ export const useGameLogStore = defineStore('GameLog', () => { watch( () => watchState.isLoggedIn, (isLoggedIn) => { - gameLogTable.value.data.length = 0; + gameLogTableData.length = 0; if (isLoggedIn) { // wait for friends to load, silly but works setTimeout(() => { @@ -130,6 +130,62 @@ export const useGameLogStore = defineStore('GameLog', () => { init(); + function getGameLogCreatedAtTs(row) { + const createdAtRaw = row?.created_at ?? row?.createdAt ?? row?.dt; + if (typeof createdAtRaw === 'number') { + const ts = + createdAtRaw > 1_000_000_000_000 + ? createdAtRaw + : createdAtRaw * 1000; + return Number.isFinite(ts) ? ts : 0; + } + + const createdAt = typeof createdAtRaw === 'string' ? createdAtRaw : ''; + const ts = dayjs(createdAt).valueOf(); + return Number.isFinite(ts) ? ts : 0; + } + + function compareGameLogRows(a, b) { + const aTs = getGameLogCreatedAtTs(a); + const bTs = getGameLogCreatedAtTs(b); + if (aTs !== bTs) { + return bTs - aTs; + } + + const aRowId = typeof a?.rowId === 'number' ? a.rowId : 0; + const bRowId = typeof b?.rowId === 'number' ? b.rowId : 0; + if (aRowId !== bRowId) { + return bRowId - aRowId; + } + + const aUid = typeof a?.uid === 'string' ? a.uid : ''; + const bUid = typeof b?.uid === 'string' ? b.uid : ''; + return aUid < bUid ? 1 : aUid > bUid ? -1 : 0; + } + + function insertGameLogSorted(entry) { + const data = gameLogTableData; + if (data.length === 0) { + data.push(entry); + return; + } + if (compareGameLogRows(entry, data[0]) < 0) { + data.unshift(entry); + return; + } + if (compareGameLogRows(entry, data[data.length - 1]) > 0) { + data.push(entry); + return; + } + for (let i = 1; i < data.length; i++) { + if (compareGameLogRows(entry, data[i]) < 0) { + data.splice(i, 0, entry); + return; + } + } + data.push(entry); + } + function clearNowPlaying() { nowPlaying.value = { url: '', @@ -146,7 +202,8 @@ export const useGameLogStore = defineStore('GameLog', () => { vrStore.updateVrNowPlaying(); } - function setNowPlaying(ctx) { + function setNowPlaying(data) { + const ctx = structuredClone(data); if (nowPlaying.value.url !== ctx.videoUrl) { if (!ctx.userId && ctx.displayName) { for (const ref of userStore.cachedUsers.values()) { @@ -308,8 +365,7 @@ export const useGameLogStore = defineStore('GameLog', () => { if (!row.userId) { return false; } - row.isFriend = friendStore.friends.has(row.userId); - return row.isFriend; + return friendStore.friends.has(row.userId); } function gameLogIsFavorite(row) { @@ -319,8 +375,7 @@ export const useGameLogStore = defineStore('GameLog', () => { if (!row.userId) { return false; } - row.isFavorite = friendStore.localFavoriteFriends.has(row.userId); - return row.isFavorite; + return friendStore.localFavoriteFriends.has(row.userId); } async function gameLogTableLookup() { @@ -347,7 +402,9 @@ export const useGameLogStore = defineStore('GameLog', () => { row.isFriend = gameLogIsFriend(row); row.isFavorite = gameLogIsFavorite(row); } - gameLogTable.value.data = shallowReactive(rows); + rows.sort(compareGameLogRows); + gameLogTableData.length = 0; + gameLogTableData.push(...rows); gameLogTable.value.loading = false; } @@ -386,7 +443,7 @@ export const useGameLogStore = defineStore('GameLog', () => { if (!gameLogSearch(entry)) { return; } - gameLogTable.value.data.push(entry); + insertGameLogSorted(entry); sweepGameLog(); uiStore.notifyMenu('game-log'); } @@ -470,10 +527,9 @@ export const useGameLogStore = defineStore('GameLog', () => { } function sweepGameLog() { - const { data } = gameLogTable.value; - const j = data.length; + const j = gameLogTableData.length; if (j > vrcxStore.maxTableSize + 50) { - data.splice(0, 50); + gameLogTableData.splice(-50, 50); } } @@ -1371,7 +1427,9 @@ export const useGameLogStore = defineStore('GameLog', () => { row.isFriend = gameLogIsFriend(row); row.isFavorite = gameLogIsFavorite(row); } - gameLogTable.value.data = shallowReactive(rows); + rows.sort(compareGameLogRows); + gameLogTableData.length = 0; + gameLogTableData.push(...rows); } return { @@ -1379,6 +1437,7 @@ export const useGameLogStore = defineStore('GameLog', () => { nowPlaying, gameLogTable, + gameLogTableData, lastVideoUrl, lastResourceloadUrl, diff --git a/src/stores/notification.js b/src/stores/notification.js index ee9a4ad0..3caf23ef 100644 --- a/src/stores/notification.js +++ b/src/stores/notification.js @@ -2197,7 +2197,8 @@ export const useNotificationStore = defineStore('Notification', () => { } } - function queueGameLogNoty(noty) { + function queueGameLogNoty(gamelog) { + const noty = structuredClone(gamelog); let bias; // remove join/leave notifications when switching worlds if ( diff --git a/src/stores/sharedFeed.js b/src/stores/sharedFeed.js index 891f5981..94553402 100644 --- a/src/stores/sharedFeed.js +++ b/src/stores/sharedFeed.js @@ -241,8 +241,8 @@ export const useSharedFeedStore = defineStore('SharedFeed', () => { rebuildOnPlayerJoining(); // also sends updated feed } - async function addEntry(feed) { - const ctx = structuredClone(feed); + async function addEntry(data) { + const ctx = structuredClone(data); const userId = ctx.userId || ctx.senderUserId; const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; if (userId === userStore.currentUser.id) { diff --git a/src/views/GameLog/GameLog.vue b/src/views/GameLog/GameLog.vue index 6b5a652f..63469885 100644 --- a/src/views/GameLog/GameLog.vue +++ b/src/views/GameLog/GameLog.vue @@ -63,8 +63,6 @@ import { storeToRefs } from 'pinia'; import { useI18n } from 'vue-i18n'; - import dayjs from 'dayjs'; - import { useAppearanceSettingsStore, useGameLogStore, useModalStore, useVrcxStore } from '../../stores'; import { DataTableLayout } from '../../components/ui/data-table'; import { InputGroupField } from '../../components/ui/input-group'; @@ -75,7 +73,7 @@ import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable'; const { gameLogTableLookup } = useGameLogStore(); - const { gameLogTable } = storeToRefs(useGameLogStore()); + const { gameLogTable, gameLogTableData } = storeToRefs(useGameLogStore()); const appearanceSettingsStore = useAppearanceSettingsStore(); const vrcxStore = useVrcxStore(); const modalStore = useModalStore(); @@ -93,39 +91,6 @@ return ''; } - function getGameLogCreatedAtTs(row) { - const createdAtRaw = row?.created_at ?? row?.createdAt ?? row?.dt; - if (typeof createdAtRaw === 'number') { - const ts = createdAtRaw > 1_000_000_000_000 ? createdAtRaw : createdAtRaw * 1000; - return Number.isFinite(ts) ? ts : 0; - } - - const createdAt = getGameLogCreatedAt(row); - const ts = dayjs(createdAt).valueOf(); - return Number.isFinite(ts) ? ts : 0; - } - - const gameLogDisplayData = computed(() => { - const data = gameLogTable.value.data; - return data.slice().sort((a, b) => { - const aTs = getGameLogCreatedAtTs(a); - const bTs = getGameLogCreatedAtTs(b); - if (aTs !== bTs) { - return bTs - aTs; - } - - const aRowId = typeof a?.rowId === 'number' ? a.rowId : 0; - const bRowId = typeof b?.rowId === 'number' ? b.rowId : 0; - if (aRowId !== bRowId) { - return bRowId - aRowId; - } - - const aUid = typeof a?.uid === 'string' ? a.uid : ''; - const bUid = typeof b?.uid === 'string' ? b.uid : ''; - return aUid < bUid ? 1 : aUid > bUid ? -1 : 0; - }); - }); - const { t } = useI18n(); const gameLogRef = ref(null); @@ -146,7 +111,7 @@ } function deleteGameLogEntry(row) { - removeFromArray(gameLogTable.value.data, row); + removeFromArray(gameLogTableData.value, row); database.deleteGameLogEntry(row); } @@ -168,7 +133,7 @@ const { table, pagination } = useVrcxVueTable({ persistKey: 'gameLog', - data: gameLogDisplayData, + data: gameLogTableData, columns, getRowId: (row, index) => `${row.type}:${row.rowId ?? index}`, initialSorting: [],