Add Avatar Wear Timers (#1086)

* Update avatar history table

* Tracking time when swapping avis

* Time spent now visible on application

* Added wear time tracking on game open/close

* Update avatar history table

* Tracking time when swapping avis

* Time spent now visible on application

* Added wear time tracking on game open/close

* Adjusted "ON CONFLICT" query

* Removed unneccesary "previousAvatar" parameter
This commit is contained in:
Tamara
2025-01-21 17:43:51 +01:00
committed by GitHub
parent 555d4adf60
commit f27d213a0d
16 changed files with 106 additions and 20 deletions

View File

@@ -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({

View File

@@ -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,

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -831,6 +831,7 @@
"last_updated": "最終更新日時",
"version": "バージョン",
"platform": "プラットフォーム",
"time_spent": "過ごした時間",
"memo": "メモ (VRCX)",
"memo_placeholder": "クリックしてメモを追加"
},

View File

@@ -808,6 +808,7 @@
"last_updated": "마지막 업데이트",
"version": "버전",
"platform": "플랫폼",
"time_spent": "방문 시간",
"memo": "VRCX 메모",
"memo_placeholder": "-"
},

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -854,6 +854,7 @@
"last_updated": "Последнее обновление",
"version": "Версия",
"platform": "Платформа",
"time_spent": "Проведенное время",
"memo": "Заметка VRCX",
"memo_placeholder": "Нажмите, чтобы добавить заметку"
},

View File

@@ -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ớ"
},

View File

@@ -856,6 +856,7 @@
"last_updated": "最后更新",
"version": "版本",
"platform": "平台",
"time_spent": "总停留时长",
"memo": "本地备注",
"memo_placeholder": "点击添加备注"
},

View File

@@ -856,6 +856,7 @@
"last_updated": "最後更新",
"version": "版本",
"platform": "平台",
"time_spent": "停留時長",
"memo": "備註",
"memo_placeholder": "點擊新增備忘錄"
},

View File

@@ -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') }}

View File

@@ -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 };
export { self as default, Database };