diff --git a/src/stores/gameLog.js b/src/stores/gameLog/index.js similarity index 72% rename from src/stores/gameLog.js rename to src/stores/gameLog/index.js index fc829aa1..65135392 100644 --- a/src/stores/gameLog.js +++ b/src/stores/gameLog/index.js @@ -8,37 +8,36 @@ import dayjs from 'dayjs'; import { compareGameLogRows, - convertYoutubeTime, findUserByDisplayName, formatSeconds, gameLogSearchFilter, getGroupName, - isRpcWorld, parseLocation, replaceBioSymbols -} from '../shared/utils'; -import { AppDebug } from '../service/appConfig'; -import { database } from '../service/database'; -import { useAdvancedSettingsStore } from './settings/advanced'; -import { useFriendStore } from './friend'; -import { useGalleryStore } from './gallery'; -import { useGameStore } from './game'; -import { useGeneralSettingsStore } from './settings/general'; -import { useInstanceStore } from './instance'; -import { useLocationStore } from './location'; -import { useModalStore } from './modal'; -import { useNotificationStore } from './notification'; -import { usePhotonStore } from './photon'; -import { useSharedFeedStore } from './sharedFeed'; -import { useUiStore } from './ui'; -import { useUserStore } from './user'; -import { useVrStore } from './vr'; -import { useVrcxStore } from './vrcx'; -import { userRequest } from '../api'; -import { watchState } from '../service/watchState'; +} from '../../shared/utils'; +import { AppDebug } from '../../service/appConfig'; +import { createMediaParsers } from './mediaParsers'; +import { database } from '../../service/database'; +import { useAdvancedSettingsStore } from '../settings/advanced'; +import { useFriendStore } from '../friend'; +import { useGalleryStore } from '../gallery'; +import { useGameStore } from '../game'; +import { useGeneralSettingsStore } from '../settings/general'; +import { useInstanceStore } from '../instance'; +import { useLocationStore } from '../location'; +import { useModalStore } from '../modal'; +import { useNotificationStore } from '../notification'; +import { usePhotonStore } from '../photon'; +import { useSharedFeedStore } from '../sharedFeed'; +import { useUiStore } from '../ui'; +import { useUserStore } from '../user'; +import { useVrStore } from '../vr'; +import { useVrcxStore } from '../vrcx'; +import { userRequest } from '../../api'; +import { watchState } from '../../service/watchState'; -import configRepository from '../service/config'; -import gameLogService from '../service/gamelog.js'; +import configRepository from '../../service/config'; +import gameLogService from '../../service/gamelog.js'; import * as workerTimers from 'worker-timers'; @@ -243,6 +242,21 @@ export const useGameLogStore = defineStore('GameLog', () => { } } + const { + addGameLogVideo, + addGameLogPyPyDance, + addGameLogVRDancing, + addGameLogZuwaZuwaDance, + addGameLogLSMedia, + addGameLogPopcornPalace + } = createMediaParsers({ + nowPlaying, + setNowPlaying, + clearNowPlaying, + userStore, + advancedSettingsStore + }); + function updateNowPlaying() { const np = nowPlaying.value; if (!nowPlaying.value.playing) { @@ -888,387 +902,6 @@ export const useGameLogStore = defineStore('GameLog', () => { } } - async function addGameLogVideo(gameLog, location, userId) { - let url; - const videoUrl = gameLog.videoUrl; - let youtubeVideoId = ''; - let videoId = ''; - let videoName = ''; - let videoLength = 0; - let displayName = ''; - let videoPos = 8; // video loading delay - if (typeof gameLog.displayName !== 'undefined') { - displayName = gameLog.displayName; - } - if (typeof gameLog.videoPos !== 'undefined') { - videoPos = gameLog.videoPos; - } - if (!isRpcWorld(location) || gameLog.videoId === 'YouTube') { - // skip PyPyDance and VRDancing videos - try { - url = new URL(videoUrl); - if ( - url.origin === 'https://t-ne.x0.to' || - url.origin === 'https://nextnex.com' || - url.origin === 'https://r.0cm.org' - ) { - url = new URL(url.searchParams.get('url')); - } - if (videoUrl.startsWith('https://u2b.cx/')) { - url = new URL(videoUrl.substring(15)); - } - const id1 = url.pathname; - const id2 = url.searchParams.get('v'); - if (id1 && id1.length === 12) { - // https://youtu.be/ - youtubeVideoId = id1.substring(1, 12); - } - if (id1 && id1.length === 19) { - // https://www.youtube.com/shorts/ - youtubeVideoId = id1.substring(8, 19); - } - if (id2 && id2.length === 11) { - // https://www.youtube.com/watch?v= - // https://music.youtube.com/watch?v= - youtubeVideoId = id2; - } - if (advancedSettingsStore.youTubeApi && youtubeVideoId) { - const data = - await advancedSettingsStore.lookupYouTubeVideo( - youtubeVideoId - ); - if (data || data.pageInfo.totalResults !== 0) { - videoId = 'YouTube'; - videoName = data.items[0].snippet.title; - videoLength = convertYoutubeTime( - data.items[0].contentDetails.duration - ); - } - } - } catch { - console.error(`Invalid URL: ${url}`); - } - const entry = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos - }; - setNowPlaying(entry); - } - } - - function addGameLogPyPyDance(gameLog, location) { - const data = - /VideoPlay\(PyPyDance\) "(.+?)",([\d.]+),([\d.]+),"(.*)"/g.exec( - gameLog.data - ); - if (!data) { - console.error('failed to parse', gameLog.data); - return; - } - const videoUrl = data[1]; - const videoPos = Number(data[2]); - const videoLength = Number(data[3]); - const title = data[4]; - const bracketArray = title.split('('); - const text1 = bracketArray.pop(); - let displayName = text1.slice(0, -1); - let text2 = bracketArray.join('('); - let videoId = ''; - if (text2 === 'Custom URL') { - videoId = 'YouTube'; - } else { - videoId = text2.substr(0, text2.indexOf(':') - 1); - text2 = text2.substr(text2.indexOf(':') + 2); - } - const videoName = text2.slice(0, -1); - if (displayName === 'Random') { - displayName = ''; - } - if (videoUrl === nowPlaying.value.url) { - const entry = { - updatedAt: gameLog.dt, - videoUrl, - videoLength, - videoPos - }; - setNowPlaying(entry); - return; - } - let userId = ''; - if (displayName) { - userId = - findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? - ''; - } - if (videoId === 'YouTube') { - const entry1 = { - dt: gameLog.dt, - videoUrl, - displayName, - videoPos, - videoId - }; - addGameLogVideo(entry1, location, userId); - } else { - const entry2 = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos - }; - setNowPlaying(entry2); - } - } - - function addGameLogVRDancing(gameLog, location) { - const data = - /VideoPlay\(VRDancing\) "(.+?)",([\d.]+),([\d.]+),(-?[\d.]+),"(.+?)","(.+?)"/g.exec( - gameLog.data - ); - if (!data) { - console.error('failed to parse', gameLog.data); - return; - } - const videoUrl = data[1]; - let videoPos = Number(data[2]); - const videoLength = Number(data[3]); - let videoId = data[4]; - const displayName = data[5]; - let videoName = data[6]; - if (videoId === '-1') { - videoId = 'YouTube'; - } - const videoNameIndex = videoName.indexOf('] '); - if (videoNameIndex !== -1) { - videoName = videoName.substring(videoNameIndex + 6); - } - if (videoPos === videoLength) { - // ummm okay - videoPos = 0; - } - if (videoUrl === nowPlaying.value.url) { - const entry = { - updatedAt: gameLog.dt, - videoUrl, - videoLength, - videoPos - }; - setNowPlaying(entry); - return; - } - let userId = ''; - if (displayName) { - userId = - findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? - ''; - } - if (videoId === 'YouTube') { - const entry1 = { - dt: gameLog.dt, - videoUrl, - displayName, - videoPos, - videoId - }; - addGameLogVideo(entry1, location, userId); - } else { - const entry2 = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos - }; - setNowPlaying(entry2); - } - } - - function addGameLogZuwaZuwaDance(gameLog, location) { - const data = - /VideoPlay\(ZuwaZuwaDance\) "(.+?)",([\d.]+),([\d.]+),(-?[\d.]+),"(.+?)","(.+?)"/g.exec( - gameLog.data - ); - if (!data) { - console.error('failed to parse', gameLog.data); - return; - } - const videoUrl = data[1]; - const videoPos = Number(data[2]); - const videoLength = Number(data[3]); - let videoId = data[4]; - let displayName = data[5]; - const videoName = data[6]; - if (displayName === 'Random') { - displayName = ''; - } - if (videoId === '9999') { - videoId = 'YouTube'; - } - if (videoUrl === nowPlaying.value.url) { - const entry = { - updatedAt: gameLog.dt, - videoUrl, - videoLength, - videoPos - }; - setNowPlaying(entry); - return; - } - let userId = ''; - if (displayName) { - userId = - findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? - ''; - } - if (videoId === 'YouTube') { - const entry1 = { - dt: gameLog.dt, - videoUrl, - displayName, - videoPos, - videoId - }; - addGameLogVideo(entry1, location, userId); - } else { - const entry2 = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos - }; - setNowPlaying(entry2); - } - } - - function addGameLogLSMedia(gameLog, location) { - // [VRCX] LSMedia 0,4268.981,Natsumi-sama,, - // [VRCX] LSMedia 0,6298.292,Natsumi-sama,The Outfit (2022), 1080p - const data = /LSMedia ([\d.]+),([\d.]+),(.+?),(.+?),(?=[^,]*$)/g.exec( - gameLog.data - ); - if (!data) { - return; - } - const videoPos = Number(data[1]); - const videoLength = Number(data[2]); - const displayName = data[3]; - const videoName = replaceBioSymbols(data[4]); - const videoUrl = videoName; - const videoId = 'LSMedia'; - if (videoUrl === nowPlaying.value.url) { - const entry = { - updatedAt: gameLog.dt, - videoUrl, - videoLength, - videoPos - }; - setNowPlaying(entry); - return; - } - let userId = ''; - if (displayName) { - userId = - findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? - ''; - } - const entry1 = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos - }; - setNowPlaying(entry1); - } - - function addGameLogPopcornPalace(gameLog, location) { - // [VRCX] VideoPlay(PopcornPalace) {"videoName": "How to Train Your Dragon - 2025-06-06", "videoPos": 37.28777, "videoLength": 11474.05, "thumbnailUrl": "", "displayName": "miner28_3", "isPaused": false, "is3D": false, "looping": false} - let data = gameLog.data; - if (!data) { - return; - } - try { - const j = data.indexOf('{'); - data = JSON.parse(data.substring(j)); - } catch (err) { - console.error('Failed to parse PopcornPalace data:', err); - return; - } - - const videoPos = Number(data.videoPos); - const videoLength = Number(data.videoLength); - const displayName = data.displayName || ''; - const videoName = data.videoName || ''; - const videoUrl = videoName; - const videoId = 'PopcornPalace'; - const thumbnailUrl = data.thumbnailUrl || ''; - if (!videoName) { - clearNowPlaying(); - return; - } - if (videoUrl === nowPlaying.value.url) { - const entry = { - updatedAt: gameLog.dt, - videoUrl, - videoLength, - videoPos, - thumbnailUrl - }; - setNowPlaying(entry); - return; - } - let userId = ''; - if (displayName) { - userId = - findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? - ''; - } - const entry1 = { - created_at: gameLog.dt, - type: 'VideoPlay', - videoUrl, - videoId, - videoName, - videoLength, - location, - displayName, - userId, - videoPos, - thumbnailUrl - }; - setNowPlaying(entry1); - } - async function getGameLogTable() { await database.initTables(); const dateTill = await database.getLastDateGameLogDatabase(); diff --git a/src/stores/gameLog/mediaParsers.js b/src/stores/gameLog/mediaParsers.js new file mode 100644 index 00000000..4e6cbb48 --- /dev/null +++ b/src/stores/gameLog/mediaParsers.js @@ -0,0 +1,415 @@ +import { + convertYoutubeTime, + findUserByDisplayName, + isRpcWorld, + replaceBioSymbols +} from '../../shared/utils'; + +/** + * Creates the media parser functions for the GameLog store. + * + * @param {object} deps + * @param {import('vue').Ref} deps.nowPlaying + * @param {Function} deps.setNowPlaying + * @param {Function} deps.clearNowPlaying + * @param {object} deps.userStore – needs `.cachedUsers` + * @param {object} deps.advancedSettingsStore – needs `.youTubeApi`, `.lookupYouTubeVideo()` + * @returns {object} The media parser functions + */ +export function createMediaParsers({ + nowPlaying, + setNowPlaying, + clearNowPlaying, + userStore, + advancedSettingsStore +}) { + async function addGameLogVideo(gameLog, location, userId) { + let url; + const videoUrl = gameLog.videoUrl; + let youtubeVideoId = ''; + let videoId = ''; + let videoName = ''; + let videoLength = 0; + let displayName = ''; + let videoPos = 8; // video loading delay + if (typeof gameLog.displayName !== 'undefined') { + displayName = gameLog.displayName; + } + if (typeof gameLog.videoPos !== 'undefined') { + videoPos = gameLog.videoPos; + } + if (!isRpcWorld(location) || gameLog.videoId === 'YouTube') { + // skip PyPyDance and VRDancing videos + try { + url = new URL(videoUrl); + if ( + url.origin === 'https://t-ne.x0.to' || + url.origin === 'https://nextnex.com' || + url.origin === 'https://r.0cm.org' + ) { + url = new URL(url.searchParams.get('url')); + } + if (videoUrl.startsWith('https://u2b.cx/')) { + url = new URL(videoUrl.substring(15)); + } + const id1 = url.pathname; + const id2 = url.searchParams.get('v'); + if (id1 && id1.length === 12) { + // https://youtu.be/ + youtubeVideoId = id1.substring(1, 12); + } + if (id1 && id1.length === 19) { + // https://www.youtube.com/shorts/ + youtubeVideoId = id1.substring(8, 19); + } + if (id2 && id2.length === 11) { + // https://www.youtube.com/watch?v= + // https://music.youtube.com/watch?v= + youtubeVideoId = id2; + } + if (advancedSettingsStore.youTubeApi && youtubeVideoId) { + const data = + await advancedSettingsStore.lookupYouTubeVideo( + youtubeVideoId + ); + if (data || data.pageInfo.totalResults !== 0) { + videoId = 'YouTube'; + videoName = data.items[0].snippet.title; + videoLength = convertYoutubeTime( + data.items[0].contentDetails.duration + ); + } + } + } catch { + console.error(`Invalid URL: ${url}`); + } + const entry = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos + }; + setNowPlaying(entry); + } + } + + function addGameLogPyPyDance(gameLog, location) { + const data = + /VideoPlay\(PyPyDance\) "(.+?)",([\d.]+),([\d.]+),"(.*)"/g.exec( + gameLog.data + ); + if (!data) { + console.error('failed to parse', gameLog.data); + return; + } + const videoUrl = data[1]; + const videoPos = Number(data[2]); + const videoLength = Number(data[3]); + const title = data[4]; + const bracketArray = title.split('('); + const text1 = bracketArray.pop(); + let displayName = text1.slice(0, -1); + let text2 = bracketArray.join('('); + let videoId = ''; + if (text2 === 'Custom URL') { + videoId = 'YouTube'; + } else { + videoId = text2.substr(0, text2.indexOf(':') - 1); + text2 = text2.substr(text2.indexOf(':') + 2); + } + const videoName = text2.slice(0, -1); + if (displayName === 'Random') { + displayName = ''; + } + if (videoUrl === nowPlaying.value.url) { + const entry = { + updatedAt: gameLog.dt, + videoUrl, + videoLength, + videoPos + }; + setNowPlaying(entry); + return; + } + let userId = ''; + if (displayName) { + userId = + findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? + ''; + } + if (videoId === 'YouTube') { + const entry1 = { + dt: gameLog.dt, + videoUrl, + displayName, + videoPos, + videoId + }; + addGameLogVideo(entry1, location, userId); + } else { + const entry2 = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos + }; + setNowPlaying(entry2); + } + } + + function addGameLogVRDancing(gameLog, location) { + const data = + /VideoPlay\(VRDancing\) "(.+?)",([\d.]+),([\d.]+),(-?[\d.]+),"(.+?)","(.+?)"/g.exec( + gameLog.data + ); + if (!data) { + console.error('failed to parse', gameLog.data); + return; + } + const videoUrl = data[1]; + let videoPos = Number(data[2]); + const videoLength = Number(data[3]); + let videoId = data[4]; + const displayName = data[5]; + let videoName = data[6]; + if (videoId === '-1') { + videoId = 'YouTube'; + } + const videoNameIndex = videoName.indexOf('] '); + if (videoNameIndex !== -1) { + videoName = videoName.substring(videoNameIndex + 6); + } + if (videoPos === videoLength) { + // ummm okay + videoPos = 0; + } + if (videoUrl === nowPlaying.value.url) { + const entry = { + updatedAt: gameLog.dt, + videoUrl, + videoLength, + videoPos + }; + setNowPlaying(entry); + return; + } + let userId = ''; + if (displayName) { + userId = + findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? + ''; + } + if (videoId === 'YouTube') { + const entry1 = { + dt: gameLog.dt, + videoUrl, + displayName, + videoPos, + videoId + }; + addGameLogVideo(entry1, location, userId); + } else { + const entry2 = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos + }; + setNowPlaying(entry2); + } + } + + function addGameLogZuwaZuwaDance(gameLog, location) { + const data = + /VideoPlay\(ZuwaZuwaDance\) "(.+?)",([\d.]+),([\d.]+),(-?[\d.]+),"(.+?)","(.+?)"/g.exec( + gameLog.data + ); + if (!data) { + console.error('failed to parse', gameLog.data); + return; + } + const videoUrl = data[1]; + const videoPos = Number(data[2]); + const videoLength = Number(data[3]); + let videoId = data[4]; + let displayName = data[5]; + const videoName = data[6]; + if (displayName === 'Random') { + displayName = ''; + } + if (videoId === '9999') { + videoId = 'YouTube'; + } + if (videoUrl === nowPlaying.value.url) { + const entry = { + updatedAt: gameLog.dt, + videoUrl, + videoLength, + videoPos + }; + setNowPlaying(entry); + return; + } + let userId = ''; + if (displayName) { + userId = + findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? + ''; + } + if (videoId === 'YouTube') { + const entry1 = { + dt: gameLog.dt, + videoUrl, + displayName, + videoPos, + videoId + }; + addGameLogVideo(entry1, location, userId); + } else { + const entry2 = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos + }; + setNowPlaying(entry2); + } + } + + function addGameLogLSMedia(gameLog, location) { + // [VRCX] LSMedia 0,4268.981,Natsumi-sama,, + // [VRCX] LSMedia 0,6298.292,Natsumi-sama,The Outfit (2022), 1080p + const data = /LSMedia ([\d.]+),([\d.]+),(.+?),(.+?),(?=[^,]*$)/g.exec( + gameLog.data + ); + if (!data) { + return; + } + const videoPos = Number(data[1]); + const videoLength = Number(data[2]); + const displayName = data[3]; + const videoName = replaceBioSymbols(data[4]); + const videoUrl = videoName; + const videoId = 'LSMedia'; + if (videoUrl === nowPlaying.value.url) { + const entry = { + updatedAt: gameLog.dt, + videoUrl, + videoLength, + videoPos + }; + setNowPlaying(entry); + return; + } + let userId = ''; + if (displayName) { + userId = + findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? + ''; + } + const entry1 = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos + }; + setNowPlaying(entry1); + } + + function addGameLogPopcornPalace(gameLog, location) { + // [VRCX] VideoPlay(PopcornPalace) {"videoName": "How to Train Your Dragon - 2025-06-06", "videoPos": 37.28777, "videoLength": 11474.05, "thumbnailUrl": "", "displayName": "miner28_3", "isPaused": false, "is3D": false, "looping": false} + let data = gameLog.data; + if (!data) { + return; + } + try { + const j = data.indexOf('{'); + data = JSON.parse(data.substring(j)); + } catch (err) { + console.error('Failed to parse PopcornPalace data:', err); + return; + } + + const videoPos = Number(data.videoPos); + const videoLength = Number(data.videoLength); + const displayName = data.displayName || ''; + const videoName = data.videoName || ''; + const videoUrl = videoName; + const videoId = 'PopcornPalace'; + const thumbnailUrl = data.thumbnailUrl || ''; + if (!videoName) { + clearNowPlaying(); + return; + } + if (videoUrl === nowPlaying.value.url) { + const entry = { + updatedAt: gameLog.dt, + videoUrl, + videoLength, + videoPos, + thumbnailUrl + }; + setNowPlaying(entry); + return; + } + let userId = ''; + if (displayName) { + userId = + findUserByDisplayName(userStore.cachedUsers, displayName)?.id ?? + ''; + } + const entry1 = { + created_at: gameLog.dt, + type: 'VideoPlay', + videoUrl, + videoId, + videoName, + videoLength, + location, + displayName, + userId, + videoPos, + thumbnailUrl + }; + setNowPlaying(entry1); + } + + return { + addGameLogVideo, + addGameLogPyPyDance, + addGameLogVRDancing, + addGameLogZuwaZuwaDance, + addGameLogLSMedia, + addGameLogPopcornPalace + }; +} diff --git a/src/stores/notification.js b/src/stores/notification/index.js similarity index 87% rename from src/stores/notification.js rename to src/stores/notification/index.js index e768c937..bd43fd46 100644 --- a/src/stores/notification.js +++ b/src/stores/notification/index.js @@ -10,50 +10,49 @@ import { checkCanInvite, escapeTag, executeWithBackoff, - extractFileId, - extractFileVersion, findUserByDisplayName, getUserMemo, parseLocation, removeFromArray, replaceBioSymbols -} from '../shared/utils'; +} from '../../shared/utils'; import { friendRequest, instanceRequest, notificationRequest, userRequest, worldRequest -} from '../api'; +} from '../../api'; import { getNotificationMessage, getUserIdFromNoty as getUserIdFromNotyBase, toNotificationText -} from '../shared/notificationMessage'; -import { database, dbVars } from '../service/database'; +} from '../../shared/notificationMessage'; +import { database, dbVars } from '../../service/database'; import { getNotificationCategory, getNotificationTs -} from '../shared/notificationCategory'; -import { AppDebug } from '../service/appConfig'; -import { useAdvancedSettingsStore } from './settings/advanced'; -import { useAppearanceSettingsStore } from './settings/appearance'; -import { useFavoriteStore } from './favorite'; -import { useFriendStore } from './friend'; -import { useGameStore } from './game'; -import { useGeneralSettingsStore } from './settings/general'; -import { useGroupStore } from './group'; -import { useInstanceStore } from './instance'; -import { useLocationStore } from './location'; -import { useModalStore } from './modal'; -import { useNotificationsSettingsStore } from './settings/notifications'; -import { useSharedFeedStore } from './sharedFeed'; -import { useUiStore } from './ui'; -import { useUserStore } from './user'; -import { useWristOverlaySettingsStore } from './settings/wristOverlay'; -import { watchState } from '../service/watchState'; +} from '../../shared/notificationCategory'; +import { AppDebug } from '../../service/appConfig'; +import { createOverlayDispatch } from './overlayDispatch'; +import { useAdvancedSettingsStore } from '../settings/advanced'; +import { useAppearanceSettingsStore } from '../settings/appearance'; +import { useFavoriteStore } from '../favorite'; +import { useFriendStore } from '../friend'; +import { useGameStore } from '../game'; +import { useGeneralSettingsStore } from '../settings/general'; +import { useGroupStore } from '../group'; +import { useInstanceStore } from '../instance'; +import { useLocationStore } from '../location'; +import { useModalStore } from '../modal'; +import { useNotificationsSettingsStore } from '../settings/notifications'; +import { useSharedFeedStore } from '../sharedFeed'; +import { useUiStore } from '../ui'; +import { useUserStore } from '../user'; +import { useWristOverlaySettingsStore } from '../settings/wristOverlay'; +import { watchState } from '../../service/watchState'; -import configRepository from '../service/config'; +import configRepository from '../../service/config'; export const useNotificationStore = defineStore('Notification', () => { const { t } = useI18n(); @@ -977,113 +976,22 @@ export const useNotificationStore = defineStore('Notification', () => { } } - /** - * - * @param {object} noty - * @returns - */ - async function notySaveImage(noty) { - const imageUrl = await notyGetImage(noty); - let fileId = extractFileId(imageUrl); - let fileVersion = extractFileVersion(imageUrl); - let imageLocation = ''; - try { - if (fileId && fileVersion) { - imageLocation = await AppApi.GetImage( - imageUrl, - fileId, - fileVersion - ); - } else if (imageUrl && imageUrl.startsWith('http')) { - fileVersion = imageUrl.split('/').pop(); // 1416226261.thumbnail-500.png - fileId = fileVersion.split('.').shift(); // 1416226261 - imageLocation = await AppApi.GetImage( - imageUrl, - fileId, - fileVersion - ); - } - } catch (err) { - console.error(imageUrl, err); - } - return imageLocation; - } + const { + notySaveImage, + displayDesktopToast, + displayOverlayNotification, + displayXSNotification, + displayOvrtNotification + } = createOverlayDispatch({ + getUserIdFromNoty, + userRequest, + notificationsSettingsStore, + advancedSettingsStore, + appearanceSettingsStore + }); - function displayDesktopToast(noty, message, image) { - const result = getNotificationMessage(noty, message); - if (result) { - desktopNotification(result.title, result.body, image); - } - } - - /** - * - * @param {string} noty - * @param {string} message - * @param {string} imageFile - */ - function displayOverlayNotification(noty, message, imageFile) { - let image = ''; - if (imageFile) { - image = `file:///${imageFile}`; - } - AppApi.ExecuteVrOverlayFunction( - 'playNoty', - JSON.stringify({ noty, message, image }) - ); - } - - /** - * - * @param {any} noty - * @param {string} message - * @param {string} image - */ - function displayXSNotification(noty, message, image) { - const result = getNotificationMessage(noty, message); - if (!result) return; - const timeout = Math.floor( - parseInt( - notificationsSettingsStore.notificationTimeout.toString(), - 10 - ) / 1000 - ); - const opacity = - parseFloat(advancedSettingsStore.notificationOpacity.toString()) / - 100; - const text = toNotificationText(result.title, result.body, noty.type); - AppApi.XSNotification('VRCX', text, timeout, opacity, image); - } - - function displayOvrtNotification( - playOvrtHudNotifications, - playOvrtWristNotifications, - noty, - message, - image - ) { - const result = getNotificationMessage(noty, message); - if (!result) return; - const timeout = Math.floor( - parseInt( - notificationsSettingsStore.notificationTimeout.toString(), - 10 - ) / 1000 - ); - const opacity = - parseFloat(advancedSettingsStore.notificationOpacity.toString()) / - 100; - const text = toNotificationText(result.title, result.body, noty.type); - AppApi.OVRTNotification( - playOvrtHudNotifications, - playOvrtWristNotifications, - 'VRCX', - text, - timeout, - opacity, - image - ); - } + // Overlay dispatch functions (notySaveImage, displayDesktopToast, etc.) + // are in ./overlayDispatch.js — destructured above via createOverlayDispatch(). /** * @@ -1102,63 +1010,6 @@ export const useNotificationStore = defineStore('Notification', () => { return ''; } - /** - * - * @param {object} noty - * @returns - */ - async function notyGetImage(noty) { - let imageUrl = ''; - const userId = getUserIdFromNoty(noty); - - if (noty.thumbnailImageUrl) { - imageUrl = noty.thumbnailImageUrl; - } else if (noty.details && noty.details.imageUrl) { - imageUrl = noty.details.imageUrl; - } else if (noty.imageUrl) { - imageUrl = noty.imageUrl; - } else if (userId && !userId.startsWith('grp_')) { - imageUrl = await userRequest - .getCachedUser({ - userId - }) - .catch((err) => { - console.error(err); - return ''; - }) - .then((args) => { - if (!args.json) { - return ''; - } - if ( - appearanceSettingsStore.displayVRCPlusIconsAsAvatar && - args.json.userIcon - ) { - return args.json.userIcon; - } - if (args.json.profilePicOverride) { - return args.json.profilePicOverride; - } - return args.json.currentAvatarThumbnailImageUrl; - }); - } - return imageUrl; - } - - /** - * - * @param {string} displayName - * @param {string} message - * @param {string} image - */ - function desktopNotification(displayName, message, image) { - if (WINDOWS) { - AppApi.DesktopNotification(displayName, message, image); - } else { - window.electron.desktopNotification(displayName, message, image); - } - } - function queueGameLogNoty(gamelog) { const noty = structuredClone(gamelog); let bias; diff --git a/src/stores/notification/overlayDispatch.js b/src/stores/notification/overlayDispatch.js new file mode 100644 index 00000000..f607226a --- /dev/null +++ b/src/stores/notification/overlayDispatch.js @@ -0,0 +1,199 @@ +import { extractFileId, extractFileVersion } from '../../shared/utils'; +import { + getNotificationMessage, + toNotificationText +} from '../../shared/notificationMessage'; + +/** + * Creates the overlay dispatch functions for the Notification store. + * + * @param {object} deps + * @param {Function} deps.getUserIdFromNoty + * @param {object} deps.userRequest + * @param {object} deps.notificationsSettingsStore + * @param {object} deps.advancedSettingsStore + * @param {object} deps.appearanceSettingsStore + * @returns {object} The overlay dispatch functions + */ +export function createOverlayDispatch({ + getUserIdFromNoty, + userRequest, + notificationsSettingsStore, + advancedSettingsStore, + appearanceSettingsStore +}) { + /** + * + * @param {object} noty + * @returns + */ + async function notySaveImage(noty) { + const imageUrl = await notyGetImage(noty); + let fileId = extractFileId(imageUrl); + let fileVersion = extractFileVersion(imageUrl); + let imageLocation = ''; + try { + if (fileId && fileVersion) { + imageLocation = await AppApi.GetImage( + imageUrl, + fileId, + fileVersion + ); + } else if (imageUrl && imageUrl.startsWith('http')) { + fileVersion = imageUrl.split('/').pop(); // 1416226261.thumbnail-500.png + fileId = fileVersion.split('.').shift(); // 1416226261 + imageLocation = await AppApi.GetImage( + imageUrl, + fileId, + fileVersion + ); + } + } catch (err) { + console.error(imageUrl, err); + } + return imageLocation; + } + + function displayDesktopToast(noty, message, image) { + const result = getNotificationMessage(noty, message); + if (result) { + desktopNotification(result.title, result.body, image); + } + } + + /** + * + * @param {string} noty + * @param {string} message + * @param {string} imageFile + */ + function displayOverlayNotification(noty, message, imageFile) { + let image = ''; + if (imageFile) { + image = `file:///${imageFile}`; + } + AppApi.ExecuteVrOverlayFunction( + 'playNoty', + JSON.stringify({ noty, message, image }) + ); + } + + /** + * + * @param {any} noty + * @param {string} message + * @param {string} image + */ + function displayXSNotification(noty, message, image) { + const result = getNotificationMessage(noty, message); + if (!result) return; + const timeout = Math.floor( + parseInt( + notificationsSettingsStore.notificationTimeout.toString(), + 10 + ) / 1000 + ); + const opacity = + parseFloat(advancedSettingsStore.notificationOpacity.toString()) / + 100; + const text = toNotificationText(result.title, result.body, noty.type); + AppApi.XSNotification('VRCX', text, timeout, opacity, image); + } + + function displayOvrtNotification( + playOvrtHudNotifications, + playOvrtWristNotifications, + noty, + message, + image + ) { + const result = getNotificationMessage(noty, message); + if (!result) return; + const timeout = Math.floor( + parseInt( + notificationsSettingsStore.notificationTimeout.toString(), + 10 + ) / 1000 + ); + const opacity = + parseFloat(advancedSettingsStore.notificationOpacity.toString()) / + 100; + const text = toNotificationText(result.title, result.body, noty.type); + AppApi.OVRTNotification( + playOvrtHudNotifications, + playOvrtWristNotifications, + 'VRCX', + text, + timeout, + opacity, + image + ); + } + + /** + * + * @param {object} noty + * @returns + */ + async function notyGetImage(noty) { + let imageUrl = ''; + const userId = getUserIdFromNoty(noty); + + if (noty.thumbnailImageUrl) { + imageUrl = noty.thumbnailImageUrl; + } else if (noty.details && noty.details.imageUrl) { + imageUrl = noty.details.imageUrl; + } else if (noty.imageUrl) { + imageUrl = noty.imageUrl; + } else if (userId && !userId.startsWith('grp_')) { + imageUrl = await userRequest + .getCachedUser({ + userId + }) + .catch((err) => { + console.error(err); + return ''; + }) + .then((args) => { + if (!args.json) { + return ''; + } + if ( + appearanceSettingsStore.displayVRCPlusIconsAsAvatar && + args.json.userIcon + ) { + return args.json.userIcon; + } + if (args.json.profilePicOverride) { + return args.json.profilePicOverride; + } + return args.json.currentAvatarThumbnailImageUrl; + }); + } + return imageUrl; + } + + /** + * + * @param {string} displayName + * @param {string} message + * @param {string} image + */ + function desktopNotification(displayName, message, image) { + if (WINDOWS) { + AppApi.DesktopNotification(displayName, message, image); + } else { + window.electron.desktopNotification(displayName, message, image); + } + } + + return { + notySaveImage, + displayDesktopToast, + displayOverlayNotification, + displayXSNotification, + displayOvrtNotification, + notyGetImage, + desktopNotification + }; +}