diff --git a/src/app.js b/src/app.js index 5bebeee8..1d00754f 100644 --- a/src/app.js +++ b/src/app.js @@ -3800,6 +3800,14 @@ console.log(`isLinux: ${LINUX}`); this.nextDiscordUpdate = 0; console.log(new Date(), 'isGameRunning', isGameRunning); } + + if (isGameRunning) { + API.currentUser.$previousAvatarSwapTime = Date.now(); + } else if (API.currentUser.$previousAvatarSwapTime) { + this.addAvatarWearTime(API.currentUser.currentAvatar); + API.currentUser.$previousAvatarSwapTime = ''; + } + if (isSteamVRRunning !== this.isSteamVRRunning) { this.isSteamVRRunning = isSteamVRRunning; console.log('isSteamVRRunning:', isSteamVRRunning); @@ -12090,6 +12098,7 @@ console.log(`isLinux: ${LINUX}`); treeData: [], bundleSizes: [], platformInfo: {}, + timeSpent: 0, lastUpdated: '', inCache: false, cacheSize: 0, @@ -12140,6 +12149,7 @@ console.log(`isLinux: ${LINUX}`); D.lastUpdated = ''; D.bundleSizes = []; D.platformInfo = {}; + D.timeSpent = 0; D.isFavorite = API.cachedFavoritesByObjectId.has(avatarId) || (this.isLocalUserVrcplusSupporter() && @@ -12158,6 +12168,9 @@ console.log(`isLinux: ${LINUX}`); return; } } + database.getAvatarTimeSpent(avatarId).then((aviTime) => { + D.timeSpent = aviTime.timeSpent; + }); API.getAvatar({ avatarId }) .then((args) => { var { ref } = args; @@ -19861,24 +19874,32 @@ console.log(`isLinux: ${LINUX}`); $app.methods.addAvatarToHistory = function (avatarId) { API.getAvatar({ avatarId }).then((args) => { var { ref } = args; + + database.addAvatarToCache(ref); + database.addAvatarToHistory(ref.id); + if (ref.authorId === API.currentUser.id) { return; } + var historyArray = this.avatarHistoryArray; for (var i = 0; i < historyArray.length; ++i) { if (historyArray[i].id === ref.id) { historyArray.splice(i, 1); } } - this.avatarHistoryArray.unshift(ref); - database.addAvatarToCache(ref); + this.avatarHistoryArray.unshift(ref); this.avatarHistory.delete(ref.id); this.avatarHistory.add(ref.id); - database.addAvatarToHistory(ref.id); }); }; + $app.methods.addAvatarWearTime = function (avatar) { + const timeSpent = Date.now() - API.currentUser.$previousAvatarSwapTime; + database.addAvatarTimeSpent(avatar, timeSpent); + }; + $app.methods.promptClearAvatarHistory = function () { this.$confirm('Continue? Clear Avatar History', 'Confirm', { confirmButtonText: 'Confirm', @@ -19904,7 +19925,7 @@ console.log(`isLinux: ${LINUX}`); ); $app.methods.updateDatabaseVersion = async function () { - var databaseVersion = 11; + var databaseVersion = 12; if (this.databaseVersion < databaseVersion) { if (this.databaseVersion) { var msgBox = this.$message({ diff --git a/src/classes/currentUser.js b/src/classes/currentUser.js index beea25a0..63368cef 100644 --- a/src/classes/currentUser.js +++ b/src/classes/currentUser.js @@ -106,7 +106,6 @@ export default class extends baseClass { travelingToInstance = json.presence.travelingToInstance; travelingToWorld = json.presence.travelingToWorld; } - this.applyUser({ allowAvatarCopying: json.allowAvatarCopying, badges: json.badges, @@ -166,6 +165,8 @@ export default class extends baseClass { if (this.isLoggedIn) { if (json.currentAvatar !== ref.currentAvatar) { $app.addAvatarToHistory(json.currentAvatar); + $app.addAvatarWearTime(ref.currentAvatar); + this.currentUser.$previousAvatarSwapTime = Date.now(); } Object.assign(ref, json); if (ref.homeLocation !== ref.$homeLocation.tag) { @@ -277,6 +278,7 @@ export default class extends baseClass { $offline_for: '', $location_at: Date.now(), $travelingToTime: Date.now(), + $previousAvatarSwapTime: Date.now(), $homeLocation: {}, $isVRCPlus: false, $isModerator: false, diff --git a/src/localization/en/en.json b/src/localization/en/en.json index 88293682..9fda9062 100644 --- a/src/localization/en/en.json +++ b/src/localization/en/en.json @@ -927,6 +927,7 @@ "last_updated": "Last Updated", "version": "Version", "platform": "Platform", + "time_spent": "Time Spent", "memo": "Memo", "memo_placeholder": "Click to add a memo" }, diff --git a/src/localization/es/en.json b/src/localization/es/en.json index 1e6d7f77..340bdb3d 100644 --- a/src/localization/es/en.json +++ b/src/localization/es/en.json @@ -848,6 +848,7 @@ "last_updated": "Actualizado por última vez", "version": "Versión", "platform": "Plataforma", + "time_spent": "Tiempo invertido", "memo": "Nota de VRCX", "memo_placeholder": "Haga clic aquí para añadir una nota" }, diff --git a/src/localization/fr/en.json b/src/localization/fr/en.json index a90b64c9..9aa573f1 100644 --- a/src/localization/fr/en.json +++ b/src/localization/fr/en.json @@ -923,6 +923,7 @@ "last_updated": "Dernière mise à jour", "version": "Version", "platform": "Plateforme", + "time_spent": "Temps passé", "memo": "Mémo", "memo_placeholder": "Cliquez pour ajouter un mémo" }, diff --git a/src/localization/hu/en.json b/src/localization/hu/en.json index b460ddfd..388e4ae1 100644 --- a/src/localization/hu/en.json +++ b/src/localization/hu/en.json @@ -808,6 +808,7 @@ "last_updated": "Last Updated", "version": "Verzió", "platform": "Platform", + "time_spent": "Time Spent", "memo": "Memo", "memo_placeholder": "Click to add a memo" }, diff --git a/src/localization/ja/en.json b/src/localization/ja/en.json index a562c828..888f6613 100644 --- a/src/localization/ja/en.json +++ b/src/localization/ja/en.json @@ -831,6 +831,7 @@ "last_updated": "最終更新日時", "version": "バージョン", "platform": "プラットフォーム", + "time_spent": "過ごした時間", "memo": "メモ (VRCX)", "memo_placeholder": "クリックしてメモを追加" }, diff --git a/src/localization/ko/en.json b/src/localization/ko/en.json index 23ddf283..611cf68f 100644 --- a/src/localization/ko/en.json +++ b/src/localization/ko/en.json @@ -808,6 +808,7 @@ "last_updated": "마지막 업데이트", "version": "버전", "platform": "플랫폼", + "time_spent": "방문 시간", "memo": "VRCX 메모", "memo_placeholder": "-" }, diff --git a/src/localization/pl/en.json b/src/localization/pl/en.json index c0944cc7..7530928c 100644 --- a/src/localization/pl/en.json +++ b/src/localization/pl/en.json @@ -808,6 +808,7 @@ "last_updated": "Ostatnia aktualizacja", "version": "Wersja", "platform": "Platforma", + "time_spent": "Spędzony czas", "memo": "Notatka VRCX", "memo_placeholder": "Kliknij, aby dodać notatkę VRCX" }, diff --git a/src/localization/pt/en.json b/src/localization/pt/en.json index e20b3a1b..53eaddd9 100644 --- a/src/localization/pt/en.json +++ b/src/localization/pt/en.json @@ -808,6 +808,7 @@ "last_updated": "Última Atualização", "version": "Versão", "platform": "Plataforma", + "time_spent": "Tempo Gasto", "memo": "Memo", "memo_placeholder": "Click to add a memo" }, diff --git a/src/localization/ru/en.json b/src/localization/ru/en.json index 6603bfeb..8434c83c 100644 --- a/src/localization/ru/en.json +++ b/src/localization/ru/en.json @@ -854,6 +854,7 @@ "last_updated": "Последнее обновление", "version": "Версия", "platform": "Платформа", + "time_spent": "Проведенное время", "memo": "Заметка VRCX", "memo_placeholder": "Нажмите, чтобы добавить заметку" }, diff --git a/src/localization/vi/en.json b/src/localization/vi/en.json index 49b78fb1..af2fbdb4 100644 --- a/src/localization/vi/en.json +++ b/src/localization/vi/en.json @@ -808,6 +808,7 @@ "last_updated": "Lần cập nhật cuối", "version": "Phiên bản", "platform": "Nền tảng", + "time_spent": "Thời gian đã ở", "memo": "Ghi nhớ", "memo_placeholder": "Bấm để tạo ghi nhớ" }, diff --git a/src/localization/zh-CN/en.json b/src/localization/zh-CN/en.json index 93dbe489..1939bc3d 100644 --- a/src/localization/zh-CN/en.json +++ b/src/localization/zh-CN/en.json @@ -856,6 +856,7 @@ "last_updated": "最后更新", "version": "版本", "platform": "平台", + "time_spent": "总停留时长", "memo": "本地备注", "memo_placeholder": "点击添加备注" }, diff --git a/src/localization/zh-TW/en.json b/src/localization/zh-TW/en.json index 2667469b..8c287824 100644 --- a/src/localization/zh-TW/en.json +++ b/src/localization/zh-TW/en.json @@ -856,6 +856,7 @@ "last_updated": "最後更新", "version": "版本", "platform": "平台", + "time_spent": "停留時長", "memo": "備註", "memo_placeholder": "點擊新增備忘錄" }, diff --git a/src/mixins/dialogs/avatarDialog.pug b/src/mixins/dialogs/avatarDialog.pug index 0f3ce103..1fca770e 100644 --- a/src/mixins/dialogs/avatarDialog.pug +++ b/src/mixins/dialogs/avatarDialog.pug @@ -101,6 +101,11 @@ mixin avatarDialog() span.name {{ $t('dialog.avatar.info.version') }} span.extra(v-if="avatarDialog.ref.version !== 0" v-text="avatarDialog.ref.version") span.extra(v-else) - + .x-friend-item(style="cursor:default") + .detail + span.name {{ $t('dialog.avatar.info.time_spent') }} + span.extra(v-if="avatarDialog.timeSpent === 0") - + span.extra(v-else) {{ timeToText(avatarDialog.timeSpent) }} .x-friend-item(style="width:100%;cursor:default") .detail span.name {{ $t('dialog.avatar.info.platform') }} diff --git a/src/repository/database.js b/src/repository/database.js index 130baf99..48440db8 100644 --- a/src/repository/database.js +++ b/src/repository/database.js @@ -40,7 +40,7 @@ class Database { `CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_moderation (user_id TEXT PRIMARY KEY, updated_at TEXT, display_name TEXT, block INTEGER, mute INTEGER)` ); await sqliteService.executeNonQuery( - `CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_avatar_history (avatar_id TEXT PRIMARY KEY, created_at TEXT)` + `CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_avatar_history (avatar_id TEXT PRIMARY KEY, created_at TEXT, time INTEGER)` ); await sqliteService.executeNonQuery( `CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)` @@ -2189,7 +2189,13 @@ class Database { addAvatarToHistory(avatarId) { sqliteService.executeNonQuery( - `INSERT OR REPLACE INTO ${Database.userPrefix}_avatar_history (avatar_id, created_at) VALUES (@avatar_id, @created_at)`, + `UPDATE ${Database.userPrefix}_avatar_history + SET created_at = @created_at, time = COALESCE(time, 0) + WHERE avatar_id = @avatar_id; + + INSERT INTO ${Database.userPrefix}_avatar_history (avatar_id, created_at, time) + SELECT @avatar_id, @created_at, 0 + WHERE NOT EXISTS (SELECT * FROM ${Database.userPrefix}_avatar_history WHERE avatar_id = @avatar_id)`, { '@avatar_id': avatarId, '@created_at': new Date().toJSON() @@ -2197,21 +2203,49 @@ class Database { ); } + async getAvatarTimeSpent(avatarId) { + var ref = { + timeSpent: 0, + avatarId + }; + await sqliteService.execute( + (row) => { + ref.timeSpent = row[0]; + }, + `SELECT time FROM ${Database.userPrefix}_avatar_history WHERE avatar_id = @avatarId`, + { + '@avatarId': avatarId + } + ); + + return ref; + } + + addAvatarTimeSpent(avatarId, timeSpent) { + sqliteService.executeNonQuery( + `UPDATE ${Database.userPrefix}_avatar_history SET time = time + @timeSpent WHERE avatar_id = @avatarId`, + { + '@avatarId': avatarId, + '@timeSpent': timeSpent + } + ); + } + async getAvatarHistory(currentUserId, limit = 100) { var data = []; await sqliteService.execute((dbRow) => { var row = { id: dbRow[0], - authorId: dbRow[4], - authorName: dbRow[5], - created_at: dbRow[6], - description: dbRow[7], - imageUrl: dbRow[8], - name: dbRow[9], - releaseStatus: dbRow[10], - thumbnailImageUrl: dbRow[11], - updated_at: dbRow[12], - version: dbRow[13] + authorId: dbRow[5], + authorName: dbRow[6], + created_at: dbRow[7], + description: dbRow[8], + imageUrl: dbRow[9], + name: dbRow[10], + releaseStatus: dbRow[11], + thumbnailImageUrl: dbRow[12], + updated_at: dbRow[13], + version: dbRow[14] }; data.push(row); }, `SELECT * FROM ${Database.userPrefix}_avatar_history INNER JOIN cache_avatar ON cache_avatar.id = ${Database.userPrefix}_avatar_history.avatar_id WHERE author_id != "${currentUserId}" ORDER BY ${Database.userPrefix}_avatar_history.created_at DESC LIMIT ${limit}`); @@ -2617,6 +2651,7 @@ class Database { // if (version === 0) { await this.updateTableForGroupNames(); await this.addFriendLogFriendNumber(); + await this.updateTableForAvatarHistory(); // } // await sqliteService.executeNonQuery('PRAGMA user_version = 1'); } @@ -2632,7 +2667,7 @@ class Database { `ALTER TABLE ${tableName} ADD friend_number INTEGER DEFAULT 0` ); } catch (e) { - e = e.toString(); + e = e.toString(); if (e.indexOf('duplicate column name') === -1) { console.error(e); } @@ -2651,7 +2686,7 @@ class Database { `ALTER TABLE ${tableName} ADD group_name TEXT DEFAULT ''` ); } catch (e) { - e = e.toString(); + e = e.toString(); if (e.indexOf('duplicate column name') === -1) { console.error(e); } @@ -2670,6 +2705,17 @@ class Database { } } + async updateTableForAvatarHistory() { + await sqliteService.execute((dbRow) => { + const columnExists = dbRow.some((row) => row.name === 'time'); + if (!columnExists) { + sqliteService.executeNonQuery( + `ALTER TABLE ${Database.userPrefix}_avatar_history ADD COLUMN 'time' INTEGER DEFAULT 0;` + ); + } + }, `PRAGMA table_info(${Database.userPrefix}_avatar_history);`); + } + async fixCancelFriendRequestTypo() { await sqliteService.executeNonQuery( `UPDATE ${Database.userPrefix}_friend_log_history SET type = 'CancelFriendRequest' WHERE type = 'CancelFriendRequst'` @@ -2728,4 +2774,4 @@ class Database { var self = new Database(); window.database = self; -export { self as default, Database }; \ No newline at end of file +export { self as default, Database };