diff --git a/src/components/Location.vue b/src/components/Location.vue index 259e2218..f92b0992 100644 --- a/src/components/Location.vue +++ b/src/components/Location.vue @@ -174,15 +174,11 @@ groupName.value = props.grouphint; } else if (L.groupId) { groupName.value = L.groupId; - getGroupName(instanceId) - .then((name) => { - if (!isDisposed && name && currentInstanceId() === L.tag) { - groupName.value = name; - } - }) - .catch((e) => { - console.error(e); - }); + getGroupName(instanceId).then((name) => { + if (!isDisposed && name && currentInstanceId() === L.tag) { + groupName.value = name; + } + }); } region.value = ''; if (!L.isOffline && !L.isPrivate && !L.isTraveling) { @@ -217,19 +213,15 @@ } const ref = cachedWorlds.get(L.worldId); if (typeof ref === 'undefined') { - getWorldName(L.worldId) - .then((name) => { - if (!isDisposed && name && currentInstanceId() === L.tag) { - if (L.instanceId) { - text.value = `${name} · ${translateAccessType(L.accessTypeName)}`; - } else { - text.value = name; - } + getWorldName(L.worldId).then((name) => { + if (!isDisposed && name && currentInstanceId() === L.tag) { + if (L.instanceId) { + text.value = `${name} · ${translateAccessType(L.accessTypeName)}`; + } else { + text.value = name; } - }) - .catch((e) => { - console.error(e); - }); + } + }); } else if (L.instanceId) { text.value = `${ref.name} · ${accessTypeLabel}`; } else { diff --git a/src/components/LocationWorld.vue b/src/components/LocationWorld.vue index c14e7855..13bf243f 100644 --- a/src/components/LocationWorld.vue +++ b/src/components/LocationWorld.vue @@ -84,15 +84,11 @@ groupName.value = props.grouphint; } else if (locObj.groupId) { groupName.value = locObj.groupId; - getGroupName(locObj.groupId) - .then((name) => { - if (name && props.locationobject.tag === locObj.tag) { - groupName.value = name; - } - }) - .catch((e) => { - console.error(e); - }); + getGroupName(locObj.groupId).then((name) => { + if (name && props.locationobject.tag === locObj.tag) { + groupName.value = name; + } + }); } else { groupName.value = ''; } diff --git a/src/service/database/feed.js b/src/service/database/feed.js index 882b674b..8f05b225 100644 --- a/src/service/database/feed.js +++ b/src/service/database/feed.js @@ -177,7 +177,12 @@ const feed = { ); }, - async lookupFeedDatabase(search, filters, vipList) { + async lookupFeedDatabase( + search, + filters, + vipList, + maxEntries = dbVars.maxTableSize + ) { search = search.replaceAll("'", "''"); if (search.startsWith('wrld_') || search.startsWith('grp_')) { return this.getFeedByInstanceId(search, filters, vipList); @@ -247,7 +252,7 @@ const feed = { groupName: dbRow[8] }; feedDatabase.push(row); - }, `SELECT * FROM ${dbVars.userPrefix}_feed_gps WHERE (display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM ${dbVars.userPrefix}_feed_gps WHERE (display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (status) { await sqliteService.execute((dbRow) => { @@ -263,7 +268,7 @@ const feed = { previousStatusDescription: dbRow[7] }; feedDatabase.push(row); - }, `SELECT * FROM ${dbVars.userPrefix}_feed_status WHERE (display_name LIKE '%${search}%' OR status LIKE '%${search}%' OR status_description LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM ${dbVars.userPrefix}_feed_status WHERE (display_name LIKE '%${search}%' OR status LIKE '%${search}%' OR status_description LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (bio) { await sqliteService.execute((dbRow) => { @@ -277,7 +282,7 @@ const feed = { previousBio: dbRow[5] }; feedDatabase.push(row); - }, `SELECT * FROM ${dbVars.userPrefix}_feed_bio WHERE (display_name LIKE '%${search}%' OR bio LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM ${dbVars.userPrefix}_feed_bio WHERE (display_name LIKE '%${search}%' OR bio LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (avatar) { var avatarQuery = ''; @@ -301,7 +306,7 @@ const feed = { previousCurrentAvatarThumbnailImageUrl: dbRow[9] }; feedDatabase.push(row); - }, `SELECT * FROM ${dbVars.userPrefix}_feed_avatar WHERE ((display_name LIKE '%${search}%' OR avatar_name LIKE '%${search}%') ${avatarQuery}) ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM ${dbVars.userPrefix}_feed_avatar WHERE ((display_name LIKE '%${search}%' OR avatar_name LIKE '%${search}%') ${avatarQuery}) ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (online || offline) { var query = ''; @@ -325,7 +330,7 @@ const feed = { groupName: dbRow[8] }; feedDatabase.push(row); - }, `SELECT * FROM ${dbVars.userPrefix}_feed_online_offline WHERE ((display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${query}) ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM ${dbVars.userPrefix}_feed_online_offline WHERE ((display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${query}) ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } var compareByCreatedAt = function (a, b) { var A = a.created_at; @@ -339,8 +344,8 @@ const feed = { return 0; }; feedDatabase.sort(compareByCreatedAt); - if (feedDatabase.length > dbVars.maxTableSize) { - feedDatabase.splice(0, feedDatabase.length - dbVars.maxTableSize); + if (feedDatabase.length > maxEntries) { + feedDatabase.splice(0, feedDatabase.length - maxEntries); } return feedDatabase; }, diff --git a/src/service/database/gameLog.js b/src/service/database/gameLog.js index 4b3dd769..bf396325 100644 --- a/src/service/database/gameLog.js +++ b/src/service/database/gameLog.js @@ -671,7 +671,12 @@ const gameLog = { * @returns {Promise} The game log data */ - async lookupGameLogDatabase(search, filters, vipList = []) { + async lookupGameLogDatabase( + search, + filters, + vipList, + maxEntries = dbVars.maxTableSize + ) { search = search.replaceAll("'", "''"); if (search.startsWith('wrld_') || search.startsWith('grp_')) { return this.getGameLogByLocation(search, filters); @@ -752,7 +757,7 @@ const gameLog = { groupName: dbRow[6] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_location WHERE world_name LIKE '%${search}%' OR group_name LIKE '%${search}%' ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_location WHERE world_name LIKE '%${search}%' OR group_name LIKE '%${search}%' ORDER BY id DESC LIMIT ${maxEntries}`); } if (onplayerjoined || onplayerleft) { var query = ''; @@ -774,7 +779,7 @@ const gameLog = { time: dbRow[6] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_join_leave WHERE (display_name LIKE '%${search}%' AND user_id != '${dbVars.userId}') ${vipQuery} ${query} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_join_leave WHERE (display_name LIKE '%${search}%' AND user_id != '${dbVars.userId}') ${vipQuery} ${query} ORDER BY id DESC LIMIT ${maxEntries}`); } if (portalspawn) { await sqliteService.execute((dbRow) => { @@ -789,7 +794,7 @@ const gameLog = { worldName: dbRow[6] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_portal_spawn WHERE (display_name LIKE '%${search}%' OR world_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_portal_spawn WHERE (display_name LIKE '%${search}%' OR world_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (msgevent) { await sqliteService.execute((dbRow) => { @@ -800,7 +805,7 @@ const gameLog = { data: dbRow[2] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_event WHERE data LIKE '%${search}%' ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_event WHERE data LIKE '%${search}%' ORDER BY id DESC LIMIT ${maxEntries}`); } if (external) { await sqliteService.execute((dbRow) => { @@ -814,7 +819,7 @@ const gameLog = { location: dbRow[5] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_external WHERE (display_name LIKE '%${search}%' OR message LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_external WHERE (display_name LIKE '%${search}%' OR message LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (videoplay) { await sqliteService.execute((dbRow) => { @@ -830,7 +835,7 @@ const gameLog = { userId: dbRow[7] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_video_play WHERE (video_url LIKE '%${search}%' OR video_name LIKE '%${search}%' OR display_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_video_play WHERE (video_url LIKE '%${search}%' OR video_name LIKE '%${search}%' OR display_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${maxEntries}`); } if (resourceload_string || resourceload_image) { var checkString = ''; @@ -850,7 +855,7 @@ const gameLog = { location: dbRow[4] }; gamelogDatabase.push(row); - }, `SELECT * FROM gamelog_resource_load WHERE resource_url LIKE '%${search}%' ${checkString} ${checkImage} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`); + }, `SELECT * FROM gamelog_resource_load WHERE resource_url LIKE '%${search}%' ${checkString} ${checkImage} ORDER BY id DESC LIMIT ${maxEntries}`); } var compareByCreatedAt = function (a, b) { var A = a.created_at; @@ -864,7 +869,9 @@ const gameLog = { return 0; }; gamelogDatabase.sort(compareByCreatedAt); - gamelogDatabase.splice(0, gamelogDatabase.length - dbVars.maxTableSize); + if (gamelogDatabase.length > maxEntries) { + gamelogDatabase.splice(0, gamelogDatabase.length - maxEntries); + } return gamelogDatabase; }, diff --git a/src/service/database/notifications.js b/src/service/database/notifications.js index 302c2023..d7aecd28 100644 --- a/src/service/database/notifications.js +++ b/src/service/database/notifications.js @@ -21,17 +21,63 @@ const notifications = { inviteMessage: dbRow[10], requestMessage: dbRow[11], responseMessage: dbRow[12] - } + }, + $isExpired: dbRow[13] === 1 }; - row.$isExpired = false; - if (dbRow[13] === 1) { - row.$isExpired = true; - } notifications.unshift(row); }, `SELECT * FROM ${dbVars.userPrefix}_notifications ORDER BY created_at DESC LIMIT ${dbVars.maxTableSize}`); return notifications; }, + async lookupNotificationDatabase( + search, + filters, + vipList, + maxEntries = dbVars.maxTableSize + ) { + search = search.replaceAll("'", "''"); + let notifications = []; + + let vipQuery = ''; + if (vipList.length > 0) { + const vipIds = vipList.map( + (userId) => `'${userId.replaceAll("'", "''")}'` + ); + vipQuery = `AND sender_user_id IN (${vipIds.join(',')})`; + } + + let filterQuery = ''; + if (filters.length > 0) { + const filterTypes = filters.map( + (type) => `'${type.replaceAll("'", "''")}'` + ); + filterQuery = `AND type IN (${filterTypes.join(',')})`; + } + + await sqliteService.execute((dbRow) => { + let row = { + id: dbRow[0], + created_at: dbRow[1], + type: dbRow[2], + senderUserId: dbRow[3], + senderUsername: dbRow[4], + receiverUserId: dbRow[5], + message: dbRow[6], + details: { + worldId: dbRow[7], + worldName: dbRow[8], + imageUrl: dbRow[9], + inviteMessage: dbRow[10], + requestMessage: dbRow[11], + responseMessage: dbRow[12] + }, + $isExpired: dbRow[13] === 1 + }; + notifications.unshift(row); + }, `SELECT * FROM ${dbVars.userPrefix}_notifications WHERE (sender_username LIKE '%${search}%' OR message LIKE '%${search}%' OR world_name LIKE '%${search}%') ${vipQuery} ${filterQuery} ORDER BY created_at DESC LIMIT ${maxEntries}`); + return notifications; + }, + addNotificationToDatabase(row) { var entry = { id: '', diff --git a/src/service/websocket.js b/src/service/websocket.js index fcdd38ef..3fafed1c 100644 --- a/src/service/websocket.js +++ b/src/service/websocket.js @@ -573,7 +573,7 @@ function handlePipeline(args) { } notificationStore.queueNotificationNoty(noty); notificationStore.notificationTable.data.push(noty); - sharedFeedStore.updateSharedFeed(true); + sharedFeedStore.addEntry(noty); break; default: diff --git a/src/shared/utils/world.js b/src/shared/utils/world.js index baeb9eb7..8888d92e 100644 --- a/src/shared/utils/world.js +++ b/src/shared/utils/world.js @@ -12,10 +12,14 @@ async function getWorldName(location) { const L = parseLocation(location); if (L.isRealInstance && L.worldId) { - const args = await worldRequest.getCachedWorld({ - worldId: L.worldId - }); - worldName = args.ref.name; + try { + const args = await worldRequest.getCachedWorld({ + worldId: L.worldId + }); + worldName = args.ref.name; + } catch (e) { + console.error('getWorldName failed location', location, e); + } } return worldName; diff --git a/src/stores/feed.js b/src/stores/feed.js index 4f09949e..bb95b4be 100644 --- a/src/stores/feed.js +++ b/src/stores/feed.js @@ -28,13 +28,10 @@ export const useFeedStore = defineStore('Feed', () => { pageSizeLinked: true }); - const feedSessionTable = ref([]); - watch( () => watchState.isLoggedIn, (isLoggedIn) => { feedTable.value.data.length = 0; - feedSessionTable.value = []; if (isLoggedIn) { initFeedTable(); } @@ -160,9 +157,7 @@ export const useFeedStore = defineStore('Feed', () => { function addFeed(feed) { notificationStore.queueFeedNoty(feed); - feedSessionTable.value.push(feed); - sweepFeedSessionTable(); - sharedFeedStore.updateSharedFeed(false); + sharedFeedStore.addEntry(feed); if ( feedTable.value.filter.length > 0 && !feedTable.value.filter.includes(feed.type) @@ -183,65 +178,21 @@ export const useFeedStore = defineStore('Feed', () => { UiStore.notifyMenu('feed'); } - function sweepFeedSessionTable() { - const data = feedSessionTable.value; - const k = data.length; - if (!k) { - return; - } - - // 24 hour limit - const date = new Date(); - date.setDate(date.getDate() - 1); - const limit = date.toJSON(); - - if (data[0].created_at < limit) { - let i = 0; - while (i < k && data[i].created_at < limit) { - ++i; - } - if (i === k) { - feedSessionTable.value = []; - return; - } - if (i) { - data.splice(0, i); - } - } - - const maxLen = Math.floor(vrcxStore.maxTableSize * 1.5); - if (maxLen > 0 && data.length > maxLen + 100) { - data.splice(0, 100); - } - } - function sweepFeed() { const { data } = feedTable.value; const j = data.length; if (j > vrcxStore.maxTableSize + 50) { data.splice(0, 50); } - - sweepFeedSessionTable(); } async function initFeedTable() { feedTable.value.loading = true; - feedTableLookup(); - - const getFeedDatabaseResult = await database.getFeedDatabase(); - if (getFeedDatabaseResult && getFeedDatabaseResult.length > 0) { - // rough, maybe 100 is enough - feedSessionTable.value = getFeedDatabaseResult.slice(-100); - } else { - feedSessionTable.value = []; - } } return { feedTable, - feedSessionTable, initFeedTable, feedTableLookup, addFeed diff --git a/src/stores/friend.js b/src/stores/friend.js index 71101cda..336b3ae8 100644 --- a/src/stores/friend.js +++ b/src/stores/friend.js @@ -993,6 +993,7 @@ export const useFriendStore = defineStore('Friend', () => { friendLogTable.value.data.push(friendLogHistory); database.addFriendLogHistory(friendLogHistory); notificationStore.queueFriendLogNoty(friendLogHistory); + sharedFeedStore.addEntry(friendLogHistory); const friendLogCurrent = { userId: id, displayName: ref.displayName, @@ -1003,7 +1004,6 @@ export const useFriendStore = defineStore('Friend', () => { database.setFriendLogCurrent(friendLogCurrent); uiStore.notifyMenu('friend-log'); deleteFriendRequest(id); - sharedFeedStore.updateSharedFeed(true); userRequest .getUser({ userId: id @@ -1067,13 +1067,13 @@ export const useFriendStore = defineStore('Friend', () => { friendLogTable.value.data.push(friendLogHistory); database.addFriendLogHistory(friendLogHistory); notificationStore.queueFriendLogNoty(friendLogHistory); + sharedFeedStore.addEntry(friendLogHistory); friendLog.delete(id); database.deleteFriendLogCurrent(id); favoriteStore.handleFavoriteDelete(id); if (!appearanceSettingsStore.hideUnfriends) { uiStore.notifyMenu('friend-log'); } - sharedFeedStore.updateSharedFeed(true); deleteFriend(id); } }); @@ -1130,6 +1130,7 @@ export const useFriendStore = defineStore('Friend', () => { notificationStore.queueFriendLogNoty( friendLogHistoryDisplayName ); + sharedFeedStore.addEntry(friendLogHistoryDisplayName); const friendLogCurrent = { userId: ref.id, displayName: ref.displayName, @@ -1140,7 +1141,6 @@ export const useFriendStore = defineStore('Friend', () => { database.setFriendLogCurrent(friendLogCurrent); ctx.displayName = ref.displayName; uiStore.notifyMenu('friend-log'); - sharedFeedStore.updateSharedFeed(true); } } if ( @@ -1176,6 +1176,7 @@ export const useFriendStore = defineStore('Friend', () => { friendLogTable.value.data.push(friendLogHistoryTrustLevel); database.addFriendLogHistory(friendLogHistoryTrustLevel); notificationStore.queueFriendLogNoty(friendLogHistoryTrustLevel); + sharedFeedStore.addEntry(friendLogHistoryTrustLevel); const friendLogCurrent2 = { userId: ref.id, displayName: ref.displayName, @@ -1185,7 +1186,6 @@ export const useFriendStore = defineStore('Friend', () => { friendLog.set(ref.id, friendLogCurrent2); database.setFriendLogCurrent(friendLogCurrent2); uiStore.notifyMenu('friend-log'); - sharedFeedStore.updateSharedFeed(true); } ctx.trustLevel = ref.$trustLevel; } diff --git a/src/stores/gameLog.js b/src/stores/gameLog.js index 77ff84a1..b387b916 100644 --- a/src/stores/gameLog.js +++ b/src/stores/gameLog.js @@ -15,7 +15,6 @@ import { import { AppDebug } from '../service/appConfig'; import { database } from '../service/database'; import { useAdvancedSettingsStore } from './settings/advanced'; -import { useAppearanceSettingsStore } from './settings/appearance'; import { useFriendStore } from './friend'; import { useGalleryStore } from './gallery'; import { useGameStore } from './game'; @@ -49,7 +48,6 @@ export const useGameLogStore = defineStore('GameLog', () => { const vrcxStore = useVrcxStore(); const advancedSettingsStore = useAdvancedSettingsStore(); const gameStore = useGameStore(); - const appearanceSettingsStore = useAppearanceSettingsStore(); const generalSettingsStore = useGeneralSettingsStore(); const galleryStore = useGalleryStore(); const photonStore = usePhotonStore(); @@ -70,8 +68,6 @@ export const useGameLogStore = defineStore('GameLog', () => { vip: false }); - const gameLogSessionTable = ref([]); - const nowPlaying = ref({ url: '', name: '', @@ -93,7 +89,6 @@ export const useGameLogStore = defineStore('GameLog', () => { () => watchState.isLoggedIn, (isLoggedIn) => { gameLogTable.value.data.length = 0; - gameLogSessionTable.value = []; if (isLoggedIn) { // wait for friends to load, silly but works setTimeout(() => { @@ -225,14 +220,15 @@ export const useGameLogStore = defineStore('GameLog', () => { workerTimers.setTimeout(() => updateNowPlaying(), 1000); } - function tryLoadPlayerList() { + async function tryLoadPlayerList() { + // TODO: make this work again if (!gameStore.isGameRunning) { return; } console.log('Loading player list from game log...'); let ctx; let i; - const data = gameLogSessionTable.value; + const data = await database.getGamelogDatabase(); if (data.length === 0) { return; } @@ -358,13 +354,6 @@ export const useGameLogStore = defineStore('GameLog', () => { function addGameLog(entry) { entry.isFriend = gameLogIsFriend(entry); entry.isFavorite = gameLogIsFavorite(entry); - gameLogSessionTable.value.push(entry); - sweepGameLogSessionTable(); - sharedFeedStore.updateSharedFeed(false); - if (entry.type === 'VideoPlay') { - // event time can be before last gameLog entry - sharedFeedStore.updateSharedFeed(true); - } // If the VIP friend filter is enabled, logs from other friends will be ignored. if ( @@ -402,38 +391,6 @@ export const useGameLogStore = defineStore('GameLog', () => { uiStore.notifyMenu('game-log'); } - function sweepGameLogSessionTable() { - const data = gameLogSessionTable.value; - const k = data.length; - if (!k) { - return; - } - - // 24 hour limit - const date = new Date(); - date.setDate(date.getDate() - 1); - const limit = date.toJSON(); - - if (data[0].created_at < limit) { - let i = 0; - while (i < k && data[i].created_at < limit) { - ++i; - } - if (i === k) { - gameLogSessionTable.value = []; - return; - } - if (i) { - data.splice(0, i); - } - } - - const maxLen = Math.floor(vrcxStore.maxTableSize * 1.5); - if (maxLen > 0 && data.length > maxLen + 100) { - data.splice(0, 100); - } - } - async function addGamelogLocationToDatabase(input) { const groupName = await getGroupName(input.location); const entry = { @@ -518,8 +475,6 @@ export const useGameLogStore = defineStore('GameLog', () => { if (j > vrcxStore.maxTableSize + 50) { data.splice(0, 50); } - - sweepGameLogSessionTable(); } function addGameLogEntry(gameLog, location) { @@ -657,22 +612,6 @@ export const useGameLogStore = defineStore('GameLog', () => { if (typeof ref1 === 'undefined') { break; } - const friendRef = friendStore.friends.get(userId); - if (typeof friendRef?.ref !== 'undefined') { - friendRef.ref.$joinCount++; - friendRef.ref.$lastSeen = new Date().toJSON(); - friendRef.ref.$timeSpent += - dayjs(gameLog.dt) - ref1.joinTime; - if ( - appearanceSettingsStore.sidebarSortMethods.includes( - 'Sort by Last Seen' - ) - ) { - // TODO: remove - friendStore.sortVIPFriends = true; - friendStore.sortOnlineFriends = true; - } - } const time = dayjs(gameLog.dt) - ref1.joinTime; locationStore.lastLocation.playerList.delete(userId); locationStore.lastLocation.friendList.delete(userId); @@ -958,13 +897,7 @@ export const useGameLogStore = defineStore('GameLog', () => { break; } if (typeof entry !== 'undefined') { - // add tag colour - if (entry.userId) { - const tagRef = userStore.customUserTags.get(entry.userId); - if (typeof tagRef !== 'undefined') { - entry.tagColour = tagRef.colour; - } - } + sharedFeedStore.addEntry(entry); notificationStore.queueGameLogNoty(entry); addGameLog(entry); } @@ -1368,8 +1301,6 @@ export const useGameLogStore = defineStore('GameLog', () => { async function getGameLogTable() { await database.initTables(); - gameLogSessionTable.value = await database.getGamelogDatabase(); - sweepGameLogSessionTable(); const dateTill = await database.getLastDateGameLogDatabase(); updateGameLog(dateTill); } @@ -1433,7 +1364,8 @@ export const useGameLogStore = defineStore('GameLog', () => { async function initGameLogTable() { const rows = await database.lookupGameLogDatabase( gameLogTable.value.search, - gameLogTable.value.filter + gameLogTable.value.filter, + [] ); for (const row of rows) { row.isFriend = gameLogIsFriend(row); @@ -1447,7 +1379,6 @@ export const useGameLogStore = defineStore('GameLog', () => { nowPlaying, gameLogTable, - gameLogSessionTable, lastVideoUrl, lastResourceloadUrl, diff --git a/src/stores/index.js b/src/stores/index.js index ab416f0f..8991c006 100644 --- a/src/stores/index.js +++ b/src/stores/index.js @@ -73,8 +73,7 @@ async function registerSentryPiniaPlugin() { GameLog: { // @ts-ignore ...state.GameLog, - gameLogTable: null, - gameLogSessionTable: null + gameLogTable: null }, Notification: { // @ts-ignore @@ -92,7 +91,7 @@ async function registerSentryPiniaPlugin() { SharedFeed: { // @ts-ignore ...state.SharedFeed, - sharedFeed: null + sharedFeedData: null }, Group: { // @ts-ignore diff --git a/src/stores/instance.js b/src/stores/instance.js index 1cf9f641..a9ada757 100644 --- a/src/stores/instance.js +++ b/src/stores/instance.js @@ -387,11 +387,15 @@ export const useInstanceStore = defineStore('Instance', () => { const L = parseLocation(location); if (L.isRealInstance && L.worldId && L.instanceId) { - const args = await instanceRequest.getCachedInstance({ - worldId: L.worldId, - instanceId: L.instanceId - }); - instanceName = args.ref.displayName; + try { + const args = await instanceRequest.getCachedInstance({ + worldId: L.worldId, + instanceId: L.instanceId + }); + instanceName = args.ref.displayName; + } catch (e) { + console.error('getInstanceName failed location', location, e); + } } return instanceName; @@ -976,8 +980,8 @@ export const useInstanceStore = defineStore('Instance', () => { uiStore.notifyMenu('notification'); } notificationStore.queueNotificationNoty(noty); + sharedFeedStore.addEntry(noty); notificationStore.notificationTable.data.push(noty); - sharedFeedStore.updateSharedFeed(true); } /** diff --git a/src/stores/location.js b/src/stores/location.js index f56d4d9c..2021aa5a 100644 --- a/src/stores/location.js +++ b/src/stores/location.js @@ -95,14 +95,14 @@ export const useLocationStore = defineStore('Location', () => { // with the current state of things, lets not run this if we don't need to return; } - let lastLocationTemp = ''; - for (let i = gameLogStore.gameLogSessionTable.length - 1; i > -1; i--) { - const item = gameLogStore.gameLogSessionTable[i]; - if (item.type === 'Location') { - lastLocationTemp = item.location; - break; - } - } + const lastLocationArray = await database.lookupGameLogDatabase( + '', + ['Location'], + [], + 1 + ); + const lastLocationTemp = + lastLocationArray.length > 0 ? lastLocationArray[0].location : ''; if (lastLocationTemp === location) { return; } diff --git a/src/stores/notification.js b/src/stores/notification.js index 24779f4e..b4983465 100644 --- a/src/stores/notification.js +++ b/src/stores/notification.js @@ -146,10 +146,10 @@ export const useNotificationStore = defineStore('Notification', () => { } unseenNotifications.value.push(ref.id); queueNotificationNoty(ref); + sharedFeedStore.addEntry(ref); } } notificationTable.value.data.push(ref); - sharedFeedStore.updateSharedFeed(true); const D = userStore.userDialog; if ( D.visible === false || diff --git a/src/stores/photon.js b/src/stores/photon.js index 5395f4dc..aad7e6e7 100644 --- a/src/stores/photon.js +++ b/src/stores/photon.js @@ -1645,6 +1645,7 @@ export const usePhotonStore = defineStore('Photon', () => { type }; notificationStore.queueModerationNoty(noty); + sharedFeedStore.addEntry(noty); const entry = { created_at: gameLogDate, userId: ref.id, @@ -1653,9 +1654,6 @@ export const usePhotonStore = defineStore('Photon', () => { }; moderationAgainstTable.value.push(entry); } - if (block || mute || block !== row.block || mute !== row.mute) { - sharedFeedStore.updateSharedFeed(true); - } if (block || mute) { database.setModeration({ userId: ref.id, diff --git a/src/stores/settings/wristOverlay.js b/src/stores/settings/wristOverlay.js index 6ee82990..c21533a6 100644 --- a/src/stores/settings/wristOverlay.js +++ b/src/stores/settings/wristOverlay.js @@ -1,11 +1,15 @@ import { defineStore } from 'pinia'; import { ref } from 'vue'; +import { useSharedFeedStore } from '../sharedFeed'; + import configRepository from '../../service/config'; export const useWristOverlaySettingsStore = defineStore( 'WristOverlaySettings', () => { + const sharedFeed = useSharedFeedStore(); + const overlayWrist = ref(true); const hidePrivateFromFeed = ref(false); const openVRAlways = ref(false); @@ -68,6 +72,7 @@ export const useWristOverlaySettingsStore = defineStore( 'VRCX_hidePrivateFromFeed', hidePrivateFromFeed.value ); + sharedFeed.loadSharedFeed(); } function setOpenVRAlways() { openVRAlways.value = !openVRAlways.value; diff --git a/src/stores/sharedFeed.js b/src/stores/sharedFeed.js index 7fd96374..233d848a 100644 --- a/src/stores/sharedFeed.js +++ b/src/stores/sharedFeed.js @@ -1,649 +1,419 @@ -import { reactive, ref } from 'vue'; import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { watch } from 'vue'; -import { groupRequest, worldRequest } from '../api'; -import { useFeedStore } from './feed'; +import { + compareByCreatedAt, + getGroupName, + getWorldName +} from '../shared/utils'; +import { database } from '../service/database'; import { useFriendStore } from './friend'; -import { useGameLogStore } from './gameLog'; -import { useGroupStore } from './group'; import { useInstanceStore } from './instance'; import { useLocationStore } from './location'; import { useModerationStore } from './moderation'; import { useNotificationStore } from './notification'; import { useNotificationsSettingsStore } from './settings/notifications'; -import { usePhotonStore } from './photon'; import { useUserStore } from './user'; -import { useWorldStore } from './world'; import { useWristOverlaySettingsStore } from './settings/wristOverlay'; import { watchState } from '../service/watchState'; -import * as workerTimers from 'worker-timers'; - export const useSharedFeedStore = defineStore('SharedFeed', () => { const friendStore = useFriendStore(); const notificationsSettingsStore = useNotificationsSettingsStore(); const locationStore = useLocationStore(); - const groupStore = useGroupStore(); const userStore = useUserStore(); const wristOverlaySettingsStore = useWristOverlaySettingsStore(); const instanceStore = useInstanceStore(); - const gameLogStore = useGameLogStore(); const moderationStore = useModerationStore(); const notificationStore = useNotificationStore(); - const feedStore = useFeedStore(); - const worldStore = useWorldStore(); - const photonStore = usePhotonStore(); - const state = reactive({ - updateSharedFeedTimer: null, - updateSharedFeedPending: false, - updateSharedFeedPendingForceUpdate: false - }); + const onPlayerJoining = ref([]); - const sharedFeed = ref({ - gameLog: { - wrist: [], - lastEntryDate: '' - }, - feedTable: { - wrist: [], - lastEntryDate: '' - }, - notificationTable: { - wrist: [], - lastEntryDate: '' - }, - friendLogTable: { - wrist: [], - lastEntryDate: '' - }, - moderationAgainstTable: { - wrist: [], - lastEntryDate: '' - }, - pendingUpdate: false - }); - - function updateSharedFeed(forceUpdate) { - if (!watchState.isFriendsLoaded) { - return; - } - // TODO: remove debounce, decouple blocked player join/leave notifications, pull data from database with filters instead of sharedFeed - if (state.updateSharedFeedTimer) { - if (forceUpdate) { - state.updateSharedFeedPendingForceUpdate = true; - } - state.updateSharedFeedPending = true; - } else { - updateSharedExecute(forceUpdate); - state.updateSharedFeedTimer = setTimeout(() => { - if (state.updateSharedFeedPending) { - updateSharedExecute( - state.updateSharedFeedPendingForceUpdate - ); - } - state.updateSharedFeedTimer = null; - }, 150); - } - } - - function updateSharedExecute(forceUpdate) { - try { - updateSharedFeedDebounce(forceUpdate); - } catch (err) { - console.error(err); - } - state.updateSharedFeedTimer = null; - state.updateSharedFeedPending = false; - state.updateSharedFeedPendingForceUpdate = false; - } - - function updateSharedFeedDebounce(forceUpdate) { - updateSharedFeedGameLog(forceUpdate); - updateSharedFeedFeedTable(forceUpdate); - updateSharedFeedNotificationTable(forceUpdate); - updateSharedFeedFriendLogTable(forceUpdate); - updateSharedFeedModerationAgainstTable(forceUpdate); - const feeds = sharedFeed.value; - if (!feeds.pendingUpdate) { - return; - } - let wristFeed = []; - wristFeed = wristFeed.concat( - feeds.gameLog.wrist, - feeds.feedTable.wrist, - feeds.notificationTable.wrist, - feeds.friendLogTable.wrist, - feeds.moderationAgainstTable.wrist - ); - // OnPlayerJoining/Traveling - userStore.currentTravelers.forEach((ref) => { + async function rebuildOnPlayerJoining() { + let newOnPlayerJoining = []; + for (const ref of userStore.currentTravelers.values()) { const isFavorite = friendStore.localFavoriteFriends.has(ref.id); if ( + locationStore.lastLocation.playerList.has(ref.id) || (notificationsSettingsStore.sharedFeedFilters.wrist - .OnPlayerJoining === 'Friends' || - (notificationsSettingsStore.sharedFeedFilters.wrist - .OnPlayerJoining === 'VIP' && - isFavorite)) && - !locationStore.lastLocation.playerList.has(ref.id) - ) { - if (ref.$location.tag === locationStore.lastLocation.location) { - var feedEntry = { - ...ref, - isFavorite, - isFriend: true, - type: 'OnPlayerJoining' - }; - wristFeed.unshift(feedEntry); - } else { - const worldRef = worldStore.cachedWorlds.get( - ref.$location.worldId - ); - let groupName = ''; - if (ref.$location.groupId) { - const groupRef = groupStore.cachedGroups.get( - ref.$location.groupId - ); - if (typeof groupRef !== 'undefined') { - groupName = groupRef.name; - } else { - // no group cache, fetch group and try again - groupRequest - .getGroup({ - groupId: ref.$location.groupId - }) - .then((args) => { - args.ref = groupStore.applyGroup(args.json); - workerTimers.setTimeout(() => { - // delay to allow for group cache to update - sharedFeed.value.pendingUpdate = true; - updateSharedFeed(false); - }, 100); - return args; - }) - .catch((err) => { - console.error(err); - }); - } - } - if (typeof worldRef !== 'undefined') { - let feedEntry = { - created_at: ref.created_at, - type: 'GPS', - userId: ref.id, - displayName: ref.displayName, - location: ref.$location.tag, - worldName: worldRef.name, - groupName, - previousLocation: '', - isFavorite, - time: 0, - isFriend: true, - isTraveling: true - }; - wristFeed.unshift(feedEntry); - } else { - // no world cache, fetch world and try again - worldRequest - .getWorld({ - worldId: ref.$location.worldId - }) - .then((args) => { - workerTimers.setTimeout(() => { - // delay to allow for world cache to update - sharedFeed.value.pendingUpdate = true; - updateSharedFeed(false); - }, 100); - return args; - }) - .catch((err) => { - console.error(err); - }); - } - } - } - }); - wristFeed.sort(function (a, b) { - if (a.created_at < b.created_at) { - return 1; - } - if (a.created_at > b.created_at) { - return -1; - } - return 0; - }); - wristFeed.splice(16); - // temp fix, tack on instance names in the worst way possible - for (let feedEntry of wristFeed) { - if (feedEntry.location) { - const instanceRef = instanceStore.cachedInstances.get( - feedEntry.location - ); - if (instanceRef?.displayName) { - feedEntry.instanceDisplayName = instanceRef.displayName; - } - } - // invites - if (feedEntry.details?.worldId) { - const instanceRef = instanceStore.cachedInstances.get( - feedEntry.details.worldId - ); - if (instanceRef?.displayName) { - feedEntry.instanceDisplayName = instanceRef.displayName; - } - } - } - AppApi.ExecuteVrOverlayFunction( - 'wristFeedUpdate', - JSON.stringify(wristFeed) - ); - userStore.applyUserDialogLocation(); - instanceStore.applyWorldDialogInstances(); - instanceStore.applyGroupDialogInstances(); - feeds.pendingUpdate = false; - } - - function updateSharedFeedGameLog(forceUpdate) { - // Location, OnPlayerJoined, OnPlayerLeft - const sessionTable = gameLogStore.gameLogSessionTable; - let i = sessionTable.length; - if (i > 0) { - if ( - sessionTable[i - 1].created_at === - sharedFeed.value.gameLog.lastEntryDate && - forceUpdate === false - ) { - return; - } - sharedFeed.value.gameLog.lastEntryDate = - sessionTable[i - 1].created_at; - } else { - return; - } - const bias = new Date(Date.now() - 1000 * 60 * 60 * 12).toJSON(); // 12 hours - const wristArr = []; - let w = 0; - const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; - let currentUserLeaveTime = 0; - let locationJoinTime = 0; - let earliestKeptTime = 0; - for (i = sessionTable.length - 1; i > -1; i--) { - const ctx = sessionTable[i]; - if (ctx.created_at < bias) { - break; - } - if (ctx.type === 'Notification') { - continue; - } - let ctxTime = 0; - if (w >= 50 && earliestKeptTime > 0) { - ctxTime = Date.parse(ctx.created_at); - if (ctxTime < earliestKeptTime - 20 * 1000) { - break; - } - } - // on Location change remove OnPlayerLeft - if (ctx.type === 'LocationDestination') { - if (!ctxTime) { - ctxTime = Date.parse(ctx.created_at); - } - currentUserLeaveTime = ctxTime; - const currentUserLeaveTimeOffset = - currentUserLeaveTime + 5 * 1000; - for (var k = w - 1; k > -1; k--) { - var feedItem = wristArr[k]; - const feedItemTime = Date.parse(feedItem.created_at); - if ( - (feedItem.type === 'OnPlayerLeft' || - feedItem.type === 'BlockedOnPlayerLeft' || - feedItem.type === 'MutedOnPlayerLeft') && - feedItemTime >= currentUserLeaveTime && - feedItemTime <= currentUserLeaveTimeOffset - ) { - wristArr.splice(k, 1); - w--; - } - } - } - // on Location change remove OnPlayerJoined - if (ctx.type === 'Location') { - if (!ctxTime) { - ctxTime = Date.parse(ctx.created_at); - } - locationJoinTime = ctxTime; - const locationJoinTimeOffset = locationJoinTime + 20 * 1000; - for (let k = w - 1; k > -1; k--) { - let feedItem = wristArr[k]; - const feedItemTime = Date.parse(feedItem.created_at); - if ( - (feedItem.type === 'OnPlayerJoined' || - feedItem.type === 'BlockedOnPlayerJoined' || - feedItem.type === 'MutedOnPlayerJoined') && - feedItemTime >= locationJoinTime && - feedItemTime <= locationJoinTimeOffset - ) { - wristArr.splice(k, 1); - w--; - } - } - } - // remove current user - if ( - (ctx.type === 'OnPlayerJoined' || - ctx.type === 'OnPlayerLeft' || - ctx.type === 'PortalSpawn') && - ctx.displayName === userStore.currentUser.displayName + .OnPlayerJoining === 'VIP' && + !isFavorite) ) { continue; } - let isFriend = false; - let isFavorite = false; - if (ctx.userId) { - isFriend = friendStore.friends.has(ctx.userId); - isFavorite = friendStore.localFavoriteFriends.has(ctx.userId); - } else if (ctx.displayName) { - for (let ref of userStore.cachedUsers.values()) { - if (ref.displayName === ctx.displayName) { - isFriend = friendStore.friends.has(ref.id); - isFavorite = friendStore.localFavoriteFriends.has( - ref.id - ); - break; - } - } - } - // add tag colour - let tagColour = ''; - if (ctx.userId) { - const tagRef = userStore.customUserTags.get(ctx.userId); - if (typeof tagRef !== 'undefined') { - tagColour = tagRef.colour; - } - } - // BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft - if (ctx.type === 'OnPlayerJoined' || ctx.type === 'OnPlayerLeft') { - for (var ref of moderationStore.cachedPlayerModerations.values()) { - if ( - ref.targetDisplayName !== ctx.displayName && - ref.sourceUserId !== ctx.userId - ) { - continue; - } - - let type = ''; - if (ref.type === 'block') { - type = `Blocked${ctx.type}`; - } else if (ref.type === 'mute') { - type = `Muted${ctx.type}`; - } else { - continue; - } - - const entry = { - created_at: ctx.created_at, - type, - displayName: ref.targetDisplayName, - userId: ref.targetUserId, - isFriend, - isFavorite - }; - if ( - wristFilter[type] && - (wristFilter[type] === 'Everyone' || - (wristFilter[type] === 'Friends' && isFriend) || - (wristFilter[type] === 'VIP' && isFavorite)) - ) { - wristArr.unshift(entry); - } - notificationStore.queueGameLogNoty(entry); - } - } - // when too many user joins happen at once when switching instances - // the "w" counter maxes out and wont add any more entries - // until the onJoins are cleared by "Location" - // e.g. if a "VideoPlay" occurs between "OnPlayerJoined" and "Location" it wont be added - if ( - w < 50 && - wristFilter[ctx.type] && - (wristFilter[ctx.type] === 'On' || - wristFilter[ctx.type] === 'Everyone' || - (wristFilter[ctx.type] === 'Friends' && isFriend) || - (wristFilter[ctx.type] === 'VIP' && isFavorite)) - ) { - wristArr.push({ - ...ctx, - tagColour, - isFriend, - isFavorite - }); - ++w; - if (!ctxTime) { - ctxTime = Date.parse(ctx.created_at); - } - if (!earliestKeptTime || ctxTime < earliestKeptTime) { - earliestKeptTime = ctxTime; - } - } - } - wristArr.splice(50); - sharedFeed.value.gameLog.wrist = wristArr; - sharedFeed.value.pendingUpdate = true; - } - - function updateSharedFeedFeedTable(forceUpdate) { - // GPS, Online, Offline, Status, Avatar - const feedSession = feedStore.feedSessionTable; - var i = feedSession.length; - if (i > 0) { - if ( - feedSession[i - 1].created_at === - sharedFeed.value.feedTable.lastEntryDate && - forceUpdate === false - ) { - return; - } - sharedFeed.value.feedTable.lastEntryDate = - feedSession[i - 1].created_at; - } else { - return; - } - const bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours - const wristArr = []; - let w = 0; - const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; - for (let i = feedSession.length - 1; i > -1; i--) { - const ctx = feedSession[i]; - if (ctx.created_at < bias) { - break; - } - if (ctx.type === 'Avatar') { - continue; - } - // hide private worlds from feed - if ( - wristOverlaySettingsStore.hidePrivateFromFeed && - ctx.type === 'GPS' && - ctx.location === 'private' - ) { - continue; - } - const isFriend = friendStore.friends.has(ctx.userId); - const isFavorite = friendStore.localFavoriteFriends.has(ctx.userId); - if ( - w < 20 && - wristFilter[ctx.type] && - (wristFilter[ctx.type] === 'Friends' || - (wristFilter[ctx.type] === 'VIP' && isFavorite)) - ) { - wristArr.push({ - ...ctx, - isFriend, - isFavorite - }); - ++w; - } - } - sharedFeed.value.feedTable.wrist = wristArr; - sharedFeed.value.pendingUpdate = true; - } - - function updateSharedFeedNotificationTable(forceUpdate) { - // invite, requestInvite, requestInviteResponse, inviteResponse, friendRequest - const notificationTable = notificationStore.notificationTable.data; - let i = notificationTable.length; - if (i > 0) { - if ( - notificationTable[i - 1].created_at === - sharedFeed.value.notificationTable.lastEntryDate && - forceUpdate === false - ) { - return; - } - sharedFeed.value.notificationTable.lastEntryDate = - notificationTable[i - 1].created_at; - } else { - return; - } - const bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours - const wristArr = []; - let w = 0; - const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; - for (i = notificationTable.length - 1; i > -1; i--) { - const ctx = notificationTable[i]; - if (ctx.created_at < bias) { - break; - } - if (ctx.senderUserId === userStore.currentUser.id) { - continue; - } - const isFriend = friendStore.friends.has(ctx.senderUserId); - const isFavorite = friendStore.localFavoriteFriends.has( - ctx.senderUserId - ); - if ( - w < 20 && - wristFilter[ctx.type] && - (wristFilter[ctx.type] === 'On' || - wristFilter[ctx.type] === 'Friends' || - (wristFilter[ctx.type] === 'VIP' && isFavorite)) - ) { - wristArr.push({ - ...ctx, - isFriend, - isFavorite - }); - ++w; - } - } - sharedFeed.value.notificationTable.wrist = wristArr; - sharedFeed.value.pendingUpdate = true; - } - - function updateSharedFeedFriendLogTable(forceUpdate) { - // TrustLevel, Friend, FriendRequest, Unfriend, DisplayName - const friendLog = friendStore.friendLogTable.data; - var i = friendLog.length; - if (i > 0) { - if ( - friendLog[i - 1].created_at === - sharedFeed.value.friendLogTable.lastEntryDate && - forceUpdate === false - ) { - return; - } - sharedFeed.value.friendLogTable.lastEntryDate = - friendLog[i - 1].created_at; - } else { - return; - } - const bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours - const wristArr = []; - let w = 0; - const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; - for (let i = friendLog.length - 1; i > -1; i--) { - const ctx = friendLog[i]; - if (ctx.created_at < bias) { - break; - } - if (ctx.type === 'FriendRequest') { - continue; - } - const isFriend = friendStore.friends.has(ctx.userId); - const isFavorite = friendStore.localFavoriteFriends.has(ctx.userId); - if ( - w < 20 && - wristFilter[ctx.type] && - (wristFilter[ctx.type] === 'On' || - wristFilter[ctx.type] === 'Friends' || - (wristFilter[ctx.type] === 'VIP' && isFavorite)) - ) { - wristArr.push({ - ...ctx, - isFriend, - isFavorite - }); - ++w; - } - } - sharedFeed.value.friendLogTable.wrist = wristArr; - sharedFeed.value.pendingUpdate = true; - } - - function updateSharedFeedModerationAgainstTable(forceUpdate) { - // Unblocked, Blocked, Muted, Unmuted - const moderationAgainst = photonStore.moderationAgainstTable; - var i = moderationAgainst.length; - if (i > 0) { - if ( - moderationAgainst[i - 1].created_at === - sharedFeed.value.moderationAgainstTable.lastEntryDate && - forceUpdate === false - ) { - return; - } - sharedFeed.value.moderationAgainstTable.lastEntryDate = - moderationAgainst[i - 1].created_at; - } else { - return; - } - const bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours - const wristArr = []; - let w = 0; - const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; - for (let i = moderationAgainst.length - 1; i > -1; i--) { - const ctx = moderationAgainst[i]; - if (ctx.created_at < bias) { - break; - } - const isFriend = friendStore.friends.has(ctx.userId); - const isFavorite = friendStore.localFavoriteFriends.has(ctx.userId); - // add tag colour - let tagColour = ''; - const tagRef = userStore.customUserTags.get(ctx.userId); - if (typeof tagRef !== 'undefined') { - tagColour = tagRef.colour; - } - if ( - w < 20 && - wristFilter[ctx.type] && - wristFilter[ctx.type] === 'On' - ) { - wristArr.push({ - ...ctx, - isFriend, + if (ref.$location.tag === locationStore.lastLocation.location) { + const feedEntry = { + ...ref, isFavorite, - tagColour - }); - ++w; + isFriend: true, + type: 'OnPlayerJoining' + }; + newOnPlayerJoining.unshift(feedEntry); + continue; + } + const worldName = await getWorldName(ref.$location.worldId); + const groupName = await getGroupName(ref.$location.groupId); + const feedEntry = { + created_at: ref.created_at, + type: 'GPS', + userId: ref.id, + displayName: ref.displayName, + location: ref.$location.tag, + worldName, + groupName, + previousLocation: '', + isFavorite, + time: 0, + isFriend: true, + isTraveling: true + }; + newOnPlayerJoining.unshift(feedEntry); + } + onPlayerJoining.value = newOnPlayerJoining; + + sharedFeedData.value = sharedFeedData.value.filter( + (ctx) => ctx.type !== 'OnPlayerJoining' + ); + sharedFeedData.value.unshift(...onPlayerJoining.value); + if (sharedFeedData.value.length > maxEntries) { + sharedFeedData.value.splice(maxEntries); + } + sendSharedFeed(); + } + + watch( + () => userStore.currentTravelers, + () => rebuildOnPlayerJoining(), + { deep: true } + ); + + watch( + () => watchState.isLoggedIn, + (isLoggedIn) => { + if (isLoggedIn) { + sharedFeedData.value = []; + loadSharedFeed(); + } + }, + { flush: 'sync' } + ); + + const sharedFeedData = ref([]); + const maxEntries = 25; + + async function loadSharedFeed() { + let newFeed = []; + const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; + // run after fav and friendlist init + const vipList = Array.from(friendStore.localFavoriteFriends.values()); + const friendList = Array.from(friendStore.friends.keys()); + + // Filters + const vipFilters = Object.keys(wristFilter).filter( + (key) => wristFilter[key] === 'VIP' + ); + const friendsFilters = Object.keys(wristFilter).filter( + (key) => wristFilter[key] === 'Friends' + ); + const everyoneFilters = Object.keys(wristFilter).filter( + (key) => + wristFilter[key] === 'On' || wristFilter[key] === 'Everyone' + ); + const everyoneAndFriendsFilters = Object.keys(wristFilter).filter( + (key) => + wristFilter[key] === 'Friends' || + wristFilter[key] === 'On' || + wristFilter[key] === 'Everyone' + ); + + // Feed + if (vipFilters.length) { + const vipFeedRows = await database.lookupFeedDatabase( + '', + vipFilters, + vipList, + maxEntries + ); + newFeed = newFeed.concat(vipFeedRows); + } + if (everyoneAndFriendsFilters.length) { + const friendsFeedRows = await database.lookupFeedDatabase( + '', + everyoneAndFriendsFilters, + [], + maxEntries + ); + newFeed = newFeed.concat(friendsFeedRows); + } + + // GameLog + if (vipFilters.length) { + const vipGameLogRows = await database.lookupGameLogDatabase( + '', + vipFilters, + vipList, + maxEntries + ); + newFeed = newFeed.concat(vipGameLogRows); + } + if (friendsFilters.length) { + const friendsGameLogRows = await database.lookupGameLogDatabase( + '', + friendsFilters, + friendList, + maxEntries + ); + newFeed = newFeed.concat(friendsGameLogRows); + } + if (everyoneFilters.length) { + const everyoneGameLogRows = await database.lookupGameLogDatabase( + '', + everyoneFilters, + [], + maxEntries + ); + newFeed = newFeed.concat(everyoneGameLogRows); + } + + // Notifications + if (vipFilters.length) { + const vipNotificationRows = + await database.lookupNotificationDatabase( + '', + vipFilters, + vipList, + maxEntries + ); + newFeed = newFeed.concat(vipNotificationRows); + } + if (everyoneAndFriendsFilters.length) { + const friendsNotificationRows = + await database.lookupNotificationDatabase( + '', + everyoneAndFriendsFilters, + [], + maxEntries + ); + newFeed = newFeed.concat(friendsNotificationRows); + } + + // hide private worlds from feed + if (wristOverlaySettingsStore.hidePrivateFromFeed) { + newFeed = newFeed.filter( + (ctx) => !(ctx.type === 'GPS' && ctx.location === 'private') + ); + } + + // remove current user + newFeed = newFeed.filter( + (ctx) => ctx.userId !== userStore.currentUser.id + ); + + // FriendLog, Moderations Against (nope, not worth it) + + newFeed.sort(compareByCreatedAt); + newFeed.splice(maxEntries); + + for (const entry of newFeed) { + const userId = entry.userId || entry.senderUserId; + entry.isFriend = false; + entry.isFavorite = false; + entry.tagColour = ''; + if (userId) { + entry.isFriend = friendStore.friends.has(userId); + entry.isFavorite = friendStore.localFavoriteFriends.has(userId); + entry.tagColour = + userStore.customUserTags.get(userId)?.colour ?? ''; + } + // tack on instance names + const location = entry.location || entry.details?.location; + if (location) { + entry.instanceDisplayName = + await instanceStore.getInstanceName(location); } } - sharedFeed.value.moderationAgainstTable.wrist = wristArr; - sharedFeed.value.pendingUpdate = true; + sharedFeedData.value = newFeed; + rebuildOnPlayerJoining(); // also sends updated feed + } + + async function addEntry(ctx) { + const userId = ctx.userId || ctx.senderUserId; + const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; + if (userId === userStore.currentUser.id) { + return; + } + if ( + wristOverlaySettingsStore.hidePrivateFromFeed && + ctx.type === 'GPS' && + ctx.location === 'private' + ) { + return; + } + if (ctx.type === 'FriendRequest' || ctx.type === 'Avatar') { + return; + } + + let isFriend = false; + let isFavorite = false; + let tagColour = ''; + if (userId) { + isFriend = friendStore.friends.has(userId); + isFavorite = friendStore.localFavoriteFriends.has(userId); + tagColour = userStore.customUserTags.get(userId)?.colour ?? ''; + } + // tack on instance names + const location = ctx.location || ctx.details?.location; + if (location) { + ctx.instanceDisplayName = + await instanceStore.getInstanceName(location); + } + + // TODO: videoPlay come through before it gets video name + + // TODO: On Location change remove OnPlayerLeft & OnPlayerJoined + { + // on Location change remove OnPlayerLeft + // if (ctx.type === 'LocationDestination') { + // if (!ctxTime) { + // ctxTime = Date.parse(ctx.created_at); + // } + // currentUserLeaveTime = ctxTime; + // const currentUserLeaveTimeOffset = currentUserLeaveTime + 5 * 1000; + // for (var k = w - 1; k > -1; k--) { + // var feedItem = wristArr[k]; + // const feedItemTime = Date.parse(feedItem.created_at); + // if ( + // (feedItem.type === 'OnPlayerLeft' || + // feedItem.type === 'BlockedOnPlayerLeft' || + // feedItem.type === 'MutedOnPlayerLeft') && + // feedItemTime >= currentUserLeaveTime && + // feedItemTime <= currentUserLeaveTimeOffset + // ) { + // wristArr.splice(k, 1); + // w--; + // } + // } + // } + // on Location change remove OnPlayerJoined + // if (ctx.type === 'Location') { + // if (!ctxTime) { + // ctxTime = Date.parse(ctx.created_at); + // } + // locationJoinTime = ctxTime; + // const locationJoinTimeOffset = locationJoinTime + 20 * 1000; + // for (let k = w - 1; k > -1; k--) { + // let feedItem = wristArr[k]; + // const feedItemTime = Date.parse(feedItem.created_at); + // if ( + // (feedItem.type === 'OnPlayerJoined' || + // feedItem.type === 'BlockedOnPlayerJoined' || + // feedItem.type === 'MutedOnPlayerJoined') && + // feedItemTime >= locationJoinTime && + // feedItemTime <= locationJoinTimeOffset + // ) { + // wristArr.splice(k, 1); + // w--; + // } + // } + // } + } + + if (ctx.type === 'OnPlayerJoined' || ctx.type === 'OnPlayerLeft') { + moderationAgainstCheck({ + ...ctx, + isFavorite, + isFriend, + tagColour + }); + } + + if ( + wristFilter[ctx.type] && + (wristFilter[ctx.type] === 'On' || + wristFilter[ctx.type] === 'Everyone' || + (wristFilter[ctx.type] === 'Friends' && isFriend) || + (wristFilter[ctx.type] === 'VIP' && isFavorite)) + ) { + addToSharedFeed({ + ...ctx, + isFavorite, + isFriend, + tagColour + }); + } + } + + function addToSharedFeed(ref) { + sharedFeedData.value.unshift(ref); + if (sharedFeedData.value.length > maxEntries) { + sharedFeedData.value.splice(maxEntries); + } + sendSharedFeed(); + } + + function moderationAgainstCheck(ctx) { + const wristFilter = notificationsSettingsStore.sharedFeedFilters.wrist; + // BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft + for (const ref of moderationStore.cachedPlayerModerations.values()) { + if (ref.sourceUserId !== ctx.userId) { + continue; + } + + let type = ''; + if (ref.type === 'block') { + type = `Blocked${ctx.type}`; + } else if (ref.type === 'mute') { + type = `Muted${ctx.type}`; + } else { + continue; + } + + const entry = { + created_at: ctx.created_at, + type, + displayName: ctx.displayName, + userId: ctx.userId, + isFavorite: ctx.isFavorite, + isFriend: ctx.isFriend, + tagColour: ctx.tagColour + }; + notificationStore.queueGameLogNoty(entry); + if ( + wristFilter[type] && + (wristFilter[type] === 'Everyone' || + (wristFilter[type] === 'Friends' && ctx.isFriend) || + (wristFilter[type] === 'VIP' && ctx.isFavorite)) + ) { + addToSharedFeed(entry); + } + } + } + + function addTag(userId, colour) { + let changed = false; + for (const entry of sharedFeedData.value) { + if (entry.userId === userId) { + entry.tagColour = colour; + changed = true; + } + } + if (changed) { + sendSharedFeed(); + } + } + + async function sendSharedFeed() { + await AppApi.ExecuteVrOverlayFunction( + 'wristFeedUpdate', + JSON.stringify(sharedFeedData.value) + ); } return { - state, - sharedFeed, - updateSharedFeed + loadSharedFeed, + sendSharedFeed, + addEntry, + addTag }; }); diff --git a/src/stores/user.js b/src/stores/user.js index 631ff013..df295cac 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -630,17 +630,11 @@ export const useUserStore = defineStore('User', () => { ...ref }); currentTravelers.set(ref.id, travelRef); - sharedFeedStore.sharedFeed.pendingUpdate = true; - sharedFeedStore.updateSharedFeed(false); onPlayerTraveling(travelRef); } } else { ref.$location = parseLocation(ref.location); - if (currentTravelers.has(ref.id)) { - currentTravelers.delete(ref.id); - sharedFeedStore.sharedFeed.pendingUpdate = true; - sharedFeedStore.updateSharedFeed(false); - } + currentTravelers.delete(ref.id); } if ( !instanceStore.cachedInstances.has(ref.$location.tag) && @@ -1651,7 +1645,7 @@ export const useUserStore = defineStore('User', () => { ref.$customTag = data.Tag; ref.$customTagColour = data.TagColour; } - sharedFeedStore.updateSharedFeed(true); + sharedFeedStore.addTag(data.UserId, data.TagColour); } async function initUserNotes() { diff --git a/src/stores/vr.js b/src/stores/vr.js index 013ecf23..833f2016 100644 --- a/src/stores/vr.js +++ b/src/stores/vr.js @@ -44,12 +44,11 @@ export const useVrStore = defineStore('Vr', () => { updateVRLastLocation(); updateVrNowPlaying(); // run these methods again to send data to the overlay - sharedFeedStore.updateSharedFeed(true); + sharedFeedStore.sendSharedFeed(); friendStore.updateOnlineFriendCounter(true); // force an update } async function saveOpenVROption() { - sharedFeedStore.updateSharedFeed(true); updateVRConfigVars(); updateVRLastLocation(); AppApi.ExecuteVrOverlayFunction('notyClear', ''); diff --git a/src/views/Charts/composables/useInstanceActivityData.js b/src/views/Charts/composables/useInstanceActivityData.js index b0685d15..e3725bba 100644 --- a/src/views/Charts/composables/useInstanceActivityData.js +++ b/src/views/Charts/composables/useInstanceActivityData.js @@ -30,15 +30,7 @@ export function useInstanceActivityData() { 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'; - } + return await getWorldName(item.location); }) ); } diff --git a/src/views/FriendsLocations/components/FriendsLocationsCard.vue b/src/views/FriendsLocations/components/FriendsLocationsCard.vue index 835ad00f..9ed7f697 100644 --- a/src/views/FriendsLocations/components/FriendsLocationsCard.vue +++ b/src/views/FriendsLocations/components/FriendsLocationsCard.vue @@ -65,7 +65,7 @@ padding: `${16 * props.cardScale * props.cardSpacing}px` })); - const avatarFallback = computed(() => props.friend.name.charAt(0) ?? '?'); + const avatarFallback = computed(() => props.friend?.name?.charAt(0) ?? '?'); const statusDotClass = computed(() => { const status = userStatusClass(props.friend.ref, props.friend.pendingOffline); diff --git a/src/views/Settings/dialogs/FeedFiltersDialog.vue b/src/views/Settings/dialogs/FeedFiltersDialog.vue index 64d247f3..39e66828 100644 --- a/src/views/Settings/dialogs/FeedFiltersDialog.vue +++ b/src/views/Settings/dialogs/FeedFiltersDialog.vue @@ -100,7 +100,7 @@ const { photonLoggingEnabled } = storeToRefs(usePhotonStore()); const { notyFeedFiltersOptions, wristFeedFiltersOptions, photonFeedFiltersOptions } = feedFiltersOptions(); const { sharedFeedFilters } = storeToRefs(useNotificationsSettingsStore()); - const { updateSharedFeed } = useSharedFeedStore(); + const { loadSharedFeed } = useSharedFeedStore(); const props = defineProps({ feedFiltersDialogMode: { @@ -136,7 +136,7 @@ function saveSharedFeedFilters() { configRepository.setString('sharedFeedFilters', JSON.stringify(sharedFeedFilters.value)); - updateSharedFeed(true); + loadSharedFeed(); } function resetNotyFeedFilters() {