mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-26 18:23:47 +02:00
492 lines
13 KiB
JavaScript
492 lines
13 KiB
JavaScript
import { reactive, ref, shallowRef, watch } from 'vue';
|
|
import { defineStore } from 'pinia';
|
|
import { useRouter } from 'vue-router';
|
|
|
|
import {
|
|
compareGameLogRows,
|
|
findUserByDisplayName,
|
|
formatSeconds,
|
|
gameLogSearchFilter,
|
|
getGroupName
|
|
} from '../../shared/utils';
|
|
import { createMediaParsers } from './mediaParsers';
|
|
import { database } from '../../services/database';
|
|
import { tryLoadPlayerList } from '../../coordinators/gameLogCoordinator';
|
|
import { useAdvancedSettingsStore } from '../settings/advanced';
|
|
import { useFriendStore } from '../friend';
|
|
import { useNotificationStore } from '../notification';
|
|
import { useUiStore } from '../ui';
|
|
import { useUserStore } from '../user';
|
|
import { useVrStore } from '../vr';
|
|
import { useVrcxStore } from '../vrcx';
|
|
import { watchState } from '../../services/watchState';
|
|
|
|
import configRepository from '../../services/config';
|
|
|
|
import * as workerTimers from 'worker-timers';
|
|
|
|
export const useGameLogStore = defineStore('GameLog', () => {
|
|
const notificationStore = useNotificationStore();
|
|
const vrStore = useVrStore();
|
|
const friendStore = useFriendStore();
|
|
const userStore = useUserStore();
|
|
const uiStore = useUiStore();
|
|
const vrcxStore = useVrcxStore();
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
|
|
const router = useRouter();
|
|
|
|
const state = reactive({
|
|
lastLocationAvatarList: new Map()
|
|
});
|
|
|
|
const gameLogTableData = shallowRef([]);
|
|
const gameLogTable = ref({
|
|
loading: false,
|
|
search: '',
|
|
filter: [],
|
|
pageSize: 20,
|
|
pageSizeLinked: true,
|
|
vip: false
|
|
});
|
|
|
|
const nowPlaying = ref({
|
|
url: '',
|
|
name: '',
|
|
length: 0,
|
|
startTime: 0,
|
|
offset: 0,
|
|
elapsed: 0,
|
|
percentage: 0,
|
|
remainingText: '',
|
|
playing: false,
|
|
thumbnailUrl: ''
|
|
});
|
|
|
|
const lastVideoUrl = ref('');
|
|
|
|
const lastResourceloadUrl = ref('');
|
|
|
|
// Latest entry ref for GameLog Widget to watch
|
|
const latestGameLogEntry = ref(null);
|
|
|
|
watch(
|
|
() => watchState.isLoggedIn,
|
|
() => {
|
|
gameLogTableData.value = [];
|
|
},
|
|
{ flush: 'sync' }
|
|
);
|
|
|
|
watch(
|
|
router.currentRoute,
|
|
(value) => {
|
|
if (value.name === 'game-log') {
|
|
initGameLogTable();
|
|
} else {
|
|
gameLogTableData.value = [];
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
watch(
|
|
() => watchState.isFavoritesLoaded,
|
|
(isFavoritesLoaded) => {
|
|
if (isFavoritesLoaded && gameLogTable.value.vip) {
|
|
gameLogTableLookup(); // re-apply VIP filter after friends are loaded
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => watchState.isFriendsLoaded,
|
|
(isFriendsLoaded) => {
|
|
if (isFriendsLoaded) {
|
|
tryLoadPlayerList();
|
|
}
|
|
},
|
|
{ flush: 'sync' }
|
|
);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
async function init() {
|
|
gameLogTable.value.filter = JSON.parse(
|
|
await configRepository.getString('VRCX_gameLogTableFilters', '[]')
|
|
);
|
|
gameLogTable.value.vip = await configRepository.getBool(
|
|
'VRCX_gameLogTableVIPFilter',
|
|
false
|
|
);
|
|
}
|
|
|
|
init();
|
|
|
|
/**
|
|
*
|
|
* @param entry
|
|
*/
|
|
function insertGameLogSorted(entry) {
|
|
const arr = gameLogTableData.value;
|
|
if (arr.length === 0) {
|
|
gameLogTableData.value = [entry];
|
|
return;
|
|
}
|
|
if (compareGameLogRows(entry, arr[0]) < 0) {
|
|
gameLogTableData.value = [entry, ...arr];
|
|
return;
|
|
}
|
|
if (compareGameLogRows(entry, arr[arr.length - 1]) > 0) {
|
|
gameLogTableData.value = [...arr, entry];
|
|
return;
|
|
}
|
|
for (let i = 1; i < arr.length; i++) {
|
|
if (compareGameLogRows(entry, arr[i]) < 0) {
|
|
gameLogTableData.value = [
|
|
...arr.slice(0, i),
|
|
entry,
|
|
...arr.slice(i)
|
|
];
|
|
return;
|
|
}
|
|
}
|
|
gameLogTableData.value = [...arr, entry];
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function clearNowPlaying() {
|
|
nowPlaying.value = {
|
|
url: '',
|
|
name: '',
|
|
length: 0,
|
|
startTime: 0,
|
|
offset: 0,
|
|
elapsed: 0,
|
|
percentage: 0,
|
|
remainingText: '',
|
|
playing: false,
|
|
thumbnailUrl: ''
|
|
};
|
|
vrStore.updateVrNowPlaying();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function resetLastMediaUrls() {
|
|
lastVideoUrl.value = '';
|
|
lastResourceloadUrl.value = '';
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param data
|
|
*/
|
|
function setNowPlaying(data) {
|
|
const ctx = structuredClone(data);
|
|
if (nowPlaying.value.url !== ctx.videoUrl) {
|
|
if (!ctx.userId && ctx.displayName) {
|
|
ctx.userId =
|
|
findUserByDisplayName(
|
|
userStore.cachedUsers,
|
|
ctx.displayName,
|
|
userStore.cachedUserIdsByDisplayName
|
|
)?.id ?? '';
|
|
}
|
|
notificationStore.queueGameLogNoty(ctx);
|
|
addGameLog(ctx);
|
|
database.addGamelogVideoPlayToDatabase(ctx);
|
|
|
|
let displayName = '';
|
|
if (ctx.displayName) {
|
|
displayName = ` (${ctx.displayName})`;
|
|
}
|
|
const name = `${ctx.videoName}${displayName}`;
|
|
nowPlaying.value = {
|
|
url: ctx.videoUrl,
|
|
name,
|
|
length: ctx.videoLength,
|
|
startTime: Date.parse(ctx.created_at) / 1000,
|
|
offset: ctx.videoPos,
|
|
elapsed: 0,
|
|
percentage: 0,
|
|
remainingText: '',
|
|
playing: false,
|
|
thumbnailUrl: ctx.thumbnailUrl
|
|
};
|
|
} else {
|
|
nowPlaying.value = {
|
|
...nowPlaying.value,
|
|
length: ctx.videoLength,
|
|
offset: ctx.videoPos,
|
|
elapsed: 0,
|
|
percentage: 0,
|
|
remainingText: '',
|
|
thumbnailUrl: ctx.thumbnailUrl
|
|
};
|
|
if (ctx.updatedAt && ctx.videoPos) {
|
|
nowPlaying.value.startTime =
|
|
Date.parse(ctx.updatedAt) / 1000 - ctx.videoPos;
|
|
} else {
|
|
nowPlaying.value.startTime =
|
|
Date.parse(ctx.created_at) / 1000 - ctx.videoPos;
|
|
}
|
|
}
|
|
vrStore.updateVrNowPlaying();
|
|
if (!nowPlaying.value.playing && ctx.videoLength > 0) {
|
|
nowPlaying.value.playing = true;
|
|
updateNowPlaying();
|
|
}
|
|
}
|
|
|
|
const {
|
|
addGameLogVideo,
|
|
addGameLogPyPyDance,
|
|
addGameLogVRDancing,
|
|
addGameLogZuwaZuwaDance,
|
|
addGameLogLSMedia,
|
|
addGameLogPopcornPalace
|
|
} = createMediaParsers({
|
|
nowPlaying,
|
|
setNowPlaying,
|
|
clearNowPlaying,
|
|
userStore,
|
|
advancedSettingsStore
|
|
});
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function updateNowPlaying() {
|
|
const np = nowPlaying.value;
|
|
if (!nowPlaying.value.playing) {
|
|
return;
|
|
}
|
|
|
|
const now = Date.now() / 1000;
|
|
np.elapsed = Math.round((now - np.startTime) * 10) / 10;
|
|
if (np.elapsed >= np.length) {
|
|
clearNowPlaying();
|
|
return;
|
|
}
|
|
np.remainingText = formatSeconds(np.length - np.elapsed);
|
|
np.percentage = Math.round(((np.elapsed * 100) / np.length) * 10) / 10;
|
|
vrStore.updateVrNowPlaying();
|
|
workerTimers.setTimeout(() => updateNowPlaying(), 1000);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param row
|
|
*/
|
|
function gameLogIsFriend(row) {
|
|
if (typeof row.isFriend !== 'undefined') {
|
|
return row.isFriend;
|
|
}
|
|
if (!row.userId) {
|
|
return false;
|
|
}
|
|
return friendStore.friends.has(row.userId);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param row
|
|
*/
|
|
function gameLogIsFavorite(row) {
|
|
if (typeof row.isFavorite !== 'undefined') {
|
|
return row.isFavorite;
|
|
}
|
|
if (!row.userId) {
|
|
return false;
|
|
}
|
|
return friendStore.localFavoriteFriends.has(row.userId);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
async function gameLogTableLookup() {
|
|
await configRepository.setString(
|
|
'VRCX_gameLogTableFilters',
|
|
JSON.stringify(gameLogTable.value.filter)
|
|
);
|
|
await configRepository.setBool(
|
|
'VRCX_gameLogTableVIPFilter',
|
|
gameLogTable.value.vip
|
|
);
|
|
gameLogTable.value.loading = true;
|
|
try {
|
|
let vipList = [];
|
|
if (gameLogTable.value.vip) {
|
|
vipList = Array.from(friendStore.localFavoriteFriends.values());
|
|
}
|
|
const search = gameLogTable.value.search.trim();
|
|
let rows = [];
|
|
if (search) {
|
|
rows = await database.searchGameLogDatabase(
|
|
search,
|
|
gameLogTable.value.filter,
|
|
vipList,
|
|
vrcxStore.searchLimit
|
|
);
|
|
} else {
|
|
rows = await database.lookupGameLogDatabase(
|
|
gameLogTable.value.filter,
|
|
vipList
|
|
);
|
|
}
|
|
|
|
for (const row of rows) {
|
|
row.isFriend = gameLogIsFriend(row);
|
|
row.isFavorite = gameLogIsFavorite(row);
|
|
}
|
|
gameLogTableData.value = rows;
|
|
} finally {
|
|
gameLogTable.value.loading = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param entry
|
|
*/
|
|
function addGameLog(entry) {
|
|
entry.isFriend = gameLogIsFriend(entry);
|
|
entry.isFavorite = gameLogIsFavorite(entry);
|
|
|
|
// Update ref for GameLog Widget (independent data stream)
|
|
latestGameLogEntry.value = entry;
|
|
|
|
// If the VIP friend filter is enabled, logs from other friends will be ignored.
|
|
if (
|
|
gameLogTable.value.vip &&
|
|
!friendStore.localFavoriteFriends.has(entry.userId) &&
|
|
(entry.type === 'OnPlayerJoined' ||
|
|
entry.type === 'OnPlayerLeft' ||
|
|
entry.type === 'VideoPlay' ||
|
|
entry.type === 'PortalSpawn' ||
|
|
entry.type === 'External')
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
entry.type === 'LocationDestination' ||
|
|
entry.type === 'AvatarChange' ||
|
|
entry.type === 'ChatBoxMessage' ||
|
|
(entry.userId === userStore.currentUser.id &&
|
|
(entry.type === 'OnPlayerJoined' ||
|
|
entry.type === 'OnPlayerLeft'))
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
gameLogTable.value.filter.length > 0 &&
|
|
!gameLogTable.value.filter.includes(entry.type)
|
|
) {
|
|
return;
|
|
}
|
|
if (!gameLogSearch(entry)) {
|
|
return;
|
|
}
|
|
insertGameLogSorted(entry);
|
|
sweepGameLog();
|
|
uiStore.notifyMenu('game-log');
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param input
|
|
*/
|
|
async function addGamelogLocationToDatabase(input) {
|
|
const groupName = await getGroupName(input.location);
|
|
const entry = {
|
|
...input,
|
|
groupName
|
|
};
|
|
database.addGamelogLocationToDatabase(entry);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param row
|
|
*/
|
|
function gameLogSearch(row) {
|
|
return gameLogSearchFilter(row, gameLogTable.value.search);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function sweepGameLog() {
|
|
const j = gameLogTableData.value.length;
|
|
if (j > vrcxStore.maxTableSize + 50) {
|
|
gameLogTableData.value = gameLogTableData.value.slice(0, -50);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
async function initGameLogTable() {
|
|
gameLogTable.value.loading = true;
|
|
const rows = await database.lookupGameLogDatabase(
|
|
gameLogTable.value.filter,
|
|
[]
|
|
);
|
|
for (const row of rows) {
|
|
row.isFriend = gameLogIsFriend(row);
|
|
row.isFavorite = gameLogIsFavorite(row);
|
|
}
|
|
gameLogTableData.value = rows;
|
|
gameLogTable.value.loading = false;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
function setLastVideoUrl(value) {
|
|
lastVideoUrl.value = value;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
function setLastResourceloadUrl(value) {
|
|
lastResourceloadUrl.value = value;
|
|
}
|
|
|
|
return {
|
|
state,
|
|
|
|
nowPlaying,
|
|
gameLogTable,
|
|
gameLogTableData,
|
|
lastVideoUrl,
|
|
lastResourceloadUrl,
|
|
latestGameLogEntry,
|
|
|
|
clearNowPlaying,
|
|
resetLastMediaUrls,
|
|
gameLogIsFriend,
|
|
gameLogIsFavorite,
|
|
gameLogTableLookup,
|
|
addGameLog,
|
|
addGamelogLocationToDatabase,
|
|
|
|
// Media parsers (used by coordinator)
|
|
addGameLogVideo,
|
|
addGameLogPyPyDance,
|
|
addGameLogVRDancing,
|
|
addGameLogZuwaZuwaDance,
|
|
addGameLogLSMedia,
|
|
addGameLogPopcornPalace,
|
|
setLastVideoUrl,
|
|
setLastResourceloadUrl
|
|
};
|
|
});
|