mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 05:43:51 +02:00
Search by userNotes, notes in playerList and friendsList
This commit is contained in:
@@ -155,6 +155,24 @@ const userReq = {
|
||||
window.API.$emit('USER:CURRENT:SAVE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param params {{ offset: number, n: number }}
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getUserNotes(params) {
|
||||
return window.API.call(`userNotes`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('USER:NOTES', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
16
src/app.js
16
src/app.js
@@ -154,6 +154,8 @@ import _groups from './classes/groups.js';
|
||||
import _vrcRegistry from './classes/vrcRegistry.js';
|
||||
import _restoreFriendOrder from './classes/restoreFriendOrder.js';
|
||||
|
||||
import { userNotes } from './classes/userNotes.js';
|
||||
|
||||
import pugTemplate from './app.pug';
|
||||
|
||||
// API classes
|
||||
@@ -721,16 +723,16 @@ console.log(`isLinux: ${LINUX}`);
|
||||
|
||||
API.applyUser = function (json) {
|
||||
var ref = this.cachedUsers.get(json.id);
|
||||
if (typeof json.statusDescription !== 'undefined') {
|
||||
if (json.statusDescription) {
|
||||
json.statusDescription = $utils.replaceBioSymbols(
|
||||
json.statusDescription
|
||||
);
|
||||
json.statusDescription = $app.removeEmojis(json.statusDescription);
|
||||
}
|
||||
if (typeof json.bio !== 'undefined') {
|
||||
if (json.bio) {
|
||||
json.bio = $utils.replaceBioSymbols(json.bio);
|
||||
}
|
||||
if (typeof json.note !== 'undefined') {
|
||||
if (json.note) {
|
||||
json.note = $utils.replaceBioSymbols(json.note);
|
||||
}
|
||||
if (json.currentAvatarImageUrl === $app.robotUrl) {
|
||||
@@ -762,7 +764,7 @@ console.log(`isLinux: ${LINUX}`);
|
||||
last_platform: '',
|
||||
location: '',
|
||||
platform: '',
|
||||
note: '',
|
||||
note: null, // keep as null, to detect deleted notes
|
||||
profilePicOverride: '',
|
||||
profilePicOverrideThumbnail: '',
|
||||
pronouns: '',
|
||||
@@ -907,6 +909,9 @@ console.log(`isLinux: ${LINUX}`);
|
||||
props[prop] = [tobe, asis];
|
||||
}
|
||||
}
|
||||
if ($ref.note !== null && $ref.note !== ref.note) {
|
||||
userNotes.checkNote(ref.id, ref.note);
|
||||
}
|
||||
// FIXME
|
||||
// if the status is offline, just ignore status and statusDescription only.
|
||||
if (has && ref.status !== 'offline' && $ref.status !== 'offline') {
|
||||
@@ -4238,6 +4243,7 @@ console.log(`isLinux: ${LINUX}`);
|
||||
}
|
||||
await $app.getAvatarHistory();
|
||||
await $app.getAllUserMemos();
|
||||
userNotes.init();
|
||||
if ($app.randomUserColours) {
|
||||
$app.getNameColour(this.currentUser.id).then((colour) => {
|
||||
this.currentUser.$userColour = colour;
|
||||
@@ -6271,7 +6277,7 @@ console.log(`isLinux: ${LINUX}`);
|
||||
);
|
||||
$app.data.hideUserMemos = await configRepository.getBool(
|
||||
'VRCX_hideUserMemos',
|
||||
false
|
||||
true
|
||||
);
|
||||
$app.data.hideUnfriends = await configRepository.getBool(
|
||||
'VRCX_hideUnfriends',
|
||||
|
||||
118
src/classes/userNotes.js
Normal file
118
src/classes/userNotes.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import { userRequest } from '../api';
|
||||
import database from '../service/database.js';
|
||||
import utils from '../classes/utils';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
const userNotes = {
|
||||
lastNoteCheck: null,
|
||||
lastDbNoteDate: null,
|
||||
notes: new Map(),
|
||||
|
||||
async init() {
|
||||
try {
|
||||
this.lastNoteCheck = new Date();
|
||||
this.notes.clear();
|
||||
// todo: get users from store
|
||||
const users = window.API.cachedUsers;
|
||||
const dbNotes = await database.getAllUserNotes();
|
||||
for (const note of dbNotes) {
|
||||
this.notes.set(note.userId, note.note);
|
||||
const user = users.get(note.userId);
|
||||
if (user) {
|
||||
user.note = note.note;
|
||||
}
|
||||
if (
|
||||
!this.lastDbNoteDate ||
|
||||
this.lastDbNoteDate < note.createdAt
|
||||
) {
|
||||
this.lastDbNoteDate = note.createdAt;
|
||||
}
|
||||
}
|
||||
await this.getLatestUserNotes();
|
||||
} catch (error) {
|
||||
console.error('Error initializing user notes:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async getLatestUserNotes() {
|
||||
this.lastNoteCheck = new Date();
|
||||
const params = {
|
||||
offset: 0,
|
||||
n: 10 // start light
|
||||
};
|
||||
const newNotes = new Map();
|
||||
let done = false;
|
||||
try {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
params.offset = i * params.n;
|
||||
const args = await userRequest.getUserNotes(params);
|
||||
for (const note of args.json) {
|
||||
if (
|
||||
this.lastDbNoteDate &&
|
||||
this.lastDbNoteDate > note.createdAt
|
||||
) {
|
||||
done = true;
|
||||
}
|
||||
if (
|
||||
!this.lastDbNoteDate ||
|
||||
this.lastDbNoteDate < note.createdAt
|
||||
) {
|
||||
this.lastDbNoteDate = note.createdAt;
|
||||
}
|
||||
note.note = utils.replaceBioSymbols(note.note);
|
||||
newNotes.set(note.targetUserId, note);
|
||||
}
|
||||
if (done || args.json.length === 0) {
|
||||
break;
|
||||
}
|
||||
params.n = 100; // crank it after first run
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 1000);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching user notes:', error);
|
||||
}
|
||||
// todo: get users from store
|
||||
const users = window.API.cachedUsers;
|
||||
|
||||
for (const note of newNotes.values()) {
|
||||
const newNote = {
|
||||
userId: note.targetUserId,
|
||||
displayName: note.targetUser?.displayName || note.targetUserId,
|
||||
note: note.note,
|
||||
createdAt: note.createdAt
|
||||
};
|
||||
await database.addUserNote(newNote);
|
||||
this.notes.set(note.targetUserId, note.note);
|
||||
const user = users.get(note.targetUserId);
|
||||
if (user) {
|
||||
user.note = note.note;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async checkNote(userId, newNote) {
|
||||
console.log('checkNote', userId, newNote);
|
||||
// last check was more than than 5 minutes ago
|
||||
if (
|
||||
!this.lastNoteCheck ||
|
||||
this.lastNoteCheck.getTime() + 5 * 60 * 1000 > Date.now()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const existingNote = this.notes.get(userId);
|
||||
if (typeof existingNote !== 'undefined' && !newNote) {
|
||||
console.log('deleting note', userId);
|
||||
this.notes.delete(userId);
|
||||
await database.deleteUserNote(userId);
|
||||
return;
|
||||
}
|
||||
if (typeof existingNote === 'undefined' || existingNote !== newNote) {
|
||||
console.log('detected note change', userId, newNote);
|
||||
await this.getLatestUserNotes();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { userNotes };
|
||||
@@ -3005,7 +3005,10 @@
|
||||
}
|
||||
const ref = API.cachedUsers.get(targetUserId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.note = _note;
|
||||
API.applyUser({
|
||||
id: targetUserId,
|
||||
note: _note
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1917,6 +1917,7 @@
|
||||
"rank": "Rank",
|
||||
"language": "Language",
|
||||
"bioLink": "Bio Links",
|
||||
"note": "Note",
|
||||
"date": "Date",
|
||||
"user": "User",
|
||||
"type": "Type",
|
||||
|
||||
@@ -43,13 +43,7 @@ class Database {
|
||||
`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)`
|
||||
);
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS world_memos (world_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
|
||||
);
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS avatar_memos (avatar_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
|
||||
`CREATE TABLE IF NOT EXISTS ${Database.userPrefix}_notes (user_id TEXT PRIMARY KEY, display_name TEXT, note TEXT, created_at TEXT)`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,6 +81,15 @@ class Database {
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS favorite_avatar (id INTEGER PRIMARY KEY, created_at TEXT, avatar_id TEXT, group_name TEXT)`
|
||||
);
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
|
||||
);
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS world_memos (world_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
|
||||
);
|
||||
await sqliteService.executeNonQuery(
|
||||
`CREATE TABLE IF NOT EXISTS avatar_memos (avatar_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
|
||||
);
|
||||
}
|
||||
|
||||
async getFeedDatabase() {
|
||||
@@ -2846,6 +2849,43 @@ class Database {
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// user notes
|
||||
|
||||
async addUserNote(note) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO ${Database.userPrefix}_notes (user_id, display_name, note, created_at) VALUES (@user_id, @display_name, @note, @created_at)`,
|
||||
{
|
||||
'@user_id': note.userId,
|
||||
'@display_name': note.displayName,
|
||||
'@note': note.note,
|
||||
'@created_at': note.createdAt
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async getAllUserNotes() {
|
||||
var data = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
userId: dbRow[0],
|
||||
displayName: dbRow[1],
|
||||
note: dbRow[2],
|
||||
createdAt: dbRow[3]
|
||||
};
|
||||
data.push(row);
|
||||
}, `SELECT user_id, display_name, note, created_at FROM ${Database.userPrefix}_notes`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteUserNote(userId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${Database.userPrefix}_notes WHERE user_id = @userId`,
|
||||
{
|
||||
'@userId': userId
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var self = new Database();
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
:placeholder="$t('view.friend_list.filter_placeholder')"
|
||||
@change="friendsListSearchChange">
|
||||
<el-option
|
||||
v-for="type in ['Display Name', 'User Name', 'Rank', 'Status', 'Bio', 'Memo']"
|
||||
v-for="type in ['Display Name', 'User Name', 'Rank', 'Status', 'Bio', 'Note', 'Memo']"
|
||||
:key="type"
|
||||
:label="type"
|
||||
:value="type"></el-option>
|
||||
@@ -347,7 +347,7 @@
|
||||
this.friendsListTable.data = [];
|
||||
let filters = [...this.friendsListSearchFilters];
|
||||
if (filters.length === 0) {
|
||||
filters = ['Display Name', 'Rank', 'Status', 'Bio', 'Memo'];
|
||||
filters = ['Display Name', 'Rank', 'Status', 'Bio', 'Note', 'Memo'];
|
||||
}
|
||||
const results = [];
|
||||
if (this.friendsListSearch) {
|
||||
@@ -379,6 +379,9 @@
|
||||
if (!match && filters.includes('Memo') && ctx.memo) {
|
||||
match = utils.localeIncludes(ctx.memo, query, this.stringComparer);
|
||||
}
|
||||
if (!match && filters.includes('Note') && ctx.ref.note) {
|
||||
match = utils.localeIncludes(ctx.ref.note, query, this.stringComparer);
|
||||
}
|
||||
if (!match && filters.includes('Bio') && ctx.ref.bio) {
|
||||
match = utils.localeIncludes(ctx.ref.bio, query, this.stringComparer);
|
||||
}
|
||||
|
||||
@@ -814,6 +814,11 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('table.playerList.note')" width="150" prop="ref.note">
|
||||
<template #default="scope">
|
||||
<span v-text="scope.row.ref.note"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</data-tables>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user