mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-02 21:16:07 +02:00
refactor: app.js (#1291)
* refactor: frontend * Fix avatar gallery sort * Update .NET dependencies * Update npm dependencies electron v37.1.0 * bulkRefreshFriends * fix dark theme * Remove crowdin * Fix config.json dialog not updating * VRCX log file fixes & add Cef log * Remove SharedVariable, fix startup * Revert init theme change * Logging date not working? Fix WinformThemer designer error * Add Cef request hander, no more escaping main page * clean * fix * fix * clean * uh * Apply thememode at startup, fixes random user colours * Split database into files * Instance info remove empty lines * Open external VRC links with VRCX * Electron fixes * fix userdialog style * ohhhh * fix store * fix store * fix: load all group members after kicking a user * fix: world dialog favorite button style * fix: Clear VRCX Cache Timer input value * clean * Fix VR overlay * Fix VR overlay 2 * Fix Discord discord rich presence for RPC worlds * Clean up age verified user tags * Fix playerList being occupied after program reload * no `this` * Fix login stuck loading * writable: false * Hide dialogs on logout * add flush sync option * rm LOGIN event * rm LOGOUT event * remove duplicate event listeners * remove duplicate event listeners * clean * remove duplicate event listeners * clean * fix theme style * fix t * clearable * clean * fix ipcEvent * Small changes * Popcorn Palace support * Remove checkActiveFriends * Clean up * Fix dragEnterCef * Block API requests when not logged in * Clear state on login & logout * Fix worldDialog instances not updating * use <script setup> * Fix avatar change event, CheckGameRunning at startup * Fix image dragging * fix * Remove PWI * fix updateLoop * add webpack-dev-server to dev environment * rm unnecessary chunks * use <script setup> * webpack-dev-server changes * use <script setup> * use <script setup> * Fix UGC text size * Split login event * t * use <script setup> * fix * Update .gitignore and enable checkJs in jsconfig * fix i18n t * use <script setup> * use <script setup> * clean * global types * fix * use checkJs for debugging * Add watchState for login watchers * fix .vue template * type fixes * rm Vue.filter * Cef v138.0.170, VC++ 2022 * Settings fixes * Remove 'USER:CURRENT' * clean up 2FA callbacks * remove userApply * rm i18n import * notification handling to use notification store methods * refactor favorite handling to use favorite store methods and clean up event emissions * refactor moderation handling to use dedicated functions for player moderation events * refactor friend handling to use dedicated functions for friend events * Fix program startup, move lang init * Fix friend state * Fix status change error * Fix user notes diff * fix * rm group event * rm auth event * rm avatar event * clean * clean * getUser * getFriends * getFavoriteWorlds, getFavoriteAvatars * AvatarGalleryUpload btn style & package.json update * Fix friend requests * Apply user * Apply world * Fix note diff * Fix VR overlay * Fixes * Update build scripts * Apply avatar * Apply instance * Apply group * update hidden VRC+ badge * Fix sameInstance "private" * fix 502/504 API errors * fix 502/504 API errors * clean * Fix friend in same instance on orange showing twice in friends list * Add back in broken friend state repair methods * add types --------- Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
const AppGlobal = Vue.observable({
|
||||
debug: false,
|
||||
debugWebSocket: false,
|
||||
debugUserDiff: false,
|
||||
debugPhotonLogging: false,
|
||||
debugGameLog: false,
|
||||
debugWebRequests: false,
|
||||
debugFriendState: false,
|
||||
errorNoty: null,
|
||||
dontLogMeOut: false,
|
||||
endpointDomain: 'https://api.vrchat.cloud/api/1',
|
||||
endpointDomainVrchat: 'https://api.vrchat.cloud/api/1',
|
||||
websocketDomain: 'wss://pipeline.vrchat.cloud',
|
||||
websocketDomainVrchat: 'wss://pipeline.vrchat.cloud'
|
||||
});
|
||||
|
||||
window.__APP_GLOBALS__ = AppGlobal;
|
||||
|
||||
export { AppGlobal };
|
||||
+140
-55
@@ -1,76 +1,161 @@
|
||||
import sqliteService from './sqlite.js';
|
||||
import sharedRepository, { SharedRepository } from './shared.js';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
var dirtyKeySet = new Set();
|
||||
|
||||
function transformKey(key) {
|
||||
return `config:${String(key).toLowerCase()}`;
|
||||
}
|
||||
|
||||
async function syncLoop() {
|
||||
if (dirtyKeySet.size > 0) {
|
||||
try {
|
||||
await sqliteService.executeNonQuery('BEGIN');
|
||||
try {
|
||||
for (var key of dirtyKeySet) {
|
||||
var value = await sharedRepository.getString(key);
|
||||
if (value === null) {
|
||||
await sqliteService.executeNonQuery(
|
||||
'DELETE FROM configs WHERE `key` = @key',
|
||||
{
|
||||
'@key': key
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await sqliteService.executeNonQuery(
|
||||
'INSERT OR REPLACE INTO configs (`key`, `value`) VALUES (@key, @value)',
|
||||
{
|
||||
'@key': key,
|
||||
'@value': value
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
dirtyKeySet.clear();
|
||||
} finally {
|
||||
await sqliteService.executeNonQuery('COMMIT');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
workerTimers.setTimeout(() => syncLoop(), 100);
|
||||
}
|
||||
|
||||
class ConfigRepository extends SharedRepository {
|
||||
class ConfigRepository {
|
||||
async init() {
|
||||
await sqliteService.executeNonQuery(
|
||||
'CREATE TABLE IF NOT EXISTS configs (`key` TEXT PRIMARY KEY, `value` TEXT)'
|
||||
);
|
||||
await sqliteService.execute(
|
||||
([key, value]) => sharedRepository.setString(key, value),
|
||||
'SELECT `key`, `value` FROM configs'
|
||||
);
|
||||
syncLoop();
|
||||
}
|
||||
|
||||
async remove(key) {
|
||||
var _key = transformKey(key);
|
||||
await sharedRepository.remove(_key);
|
||||
dirtyKeySet.add(_key);
|
||||
const _key = transformKey(key);
|
||||
await sqliteService.executeNonQuery(
|
||||
`DELETE FROM configs WHERE key = @key`,
|
||||
{
|
||||
'@key': _key
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getString(key, defaultValue = null) {
|
||||
var _key = transformKey(key);
|
||||
return sharedRepository.getString(_key, defaultValue);
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string} defaultValue
|
||||
* @returns {Promise<string | null>}
|
||||
*/
|
||||
async getString(key, defaultValue = null) {
|
||||
const _key = transformKey(key);
|
||||
let value = undefined;
|
||||
await sqliteService.execute(
|
||||
(row) => {
|
||||
value = row[0];
|
||||
},
|
||||
`SELECT value FROM configs WHERE key = @key`,
|
||||
{
|
||||
'@key': _key
|
||||
}
|
||||
);
|
||||
|
||||
if (value === null || value === undefined || value === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async setString(key, value) {
|
||||
var _key = transformKey(key);
|
||||
var _value = String(value);
|
||||
await sharedRepository.setString(_key, _value);
|
||||
dirtyKeySet.add(_key);
|
||||
const _key = transformKey(key);
|
||||
const _value = String(value);
|
||||
await sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO configs (key, value) VALUES (@key, @value)`,
|
||||
{
|
||||
'@key': _key,
|
||||
'@value': _value
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {boolean} defaultValue
|
||||
* @returns {Promise<boolean | null>}
|
||||
*/
|
||||
async getBool(key, defaultValue = null) {
|
||||
const value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {boolean} value
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async setBool(key, value) {
|
||||
await this.setString(key, value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {number} defaultValue
|
||||
* @returns {Promise<number | null>}
|
||||
*/
|
||||
async getInt(key, defaultValue = null) {
|
||||
let value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setInt(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getFloat(key, defaultValue = null) {
|
||||
let value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseFloat(value);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setFloat(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getObject(key, defaultValue = null) {
|
||||
let value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch {
|
||||
// ignore JSON parse errors
|
||||
}
|
||||
if (value !== Object(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setObject(key, value) {
|
||||
await this.setString(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {Array} defaultValue
|
||||
* @returns {Promise<Array | null>}
|
||||
*/
|
||||
async getArray(key, defaultValue = null) {
|
||||
const value = await this.getObject(key, null);
|
||||
if (Array.isArray(value) === false) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setArray(key, value) {
|
||||
await this.setObject(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+59
-2808
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,205 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const avatarFavorites = {
|
||||
addAvatarToCache(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO cache_avatar (id, added_at, author_id, author_name, created_at, description, image_url, name, release_status, thumbnail_image_url, updated_at, version) VALUES (@id, @added_at, @author_id, @author_name, @created_at, @description, @image_url, @name, @release_status, @thumbnail_image_url, @updated_at, @version)`,
|
||||
{
|
||||
'@id': entry.id,
|
||||
'@added_at': new Date().toJSON(),
|
||||
'@author_id': entry.authorId,
|
||||
'@author_name': entry.authorName,
|
||||
'@created_at': entry.created_at,
|
||||
'@description': entry.description,
|
||||
'@image_url': entry.imageUrl,
|
||||
'@name': entry.name,
|
||||
'@release_status': entry.releaseStatus,
|
||||
'@thumbnail_image_url': entry.thumbnailImageUrl,
|
||||
'@updated_at': entry.updated_at,
|
||||
'@version': entry.version
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addAvatarToHistory(avatarId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT INTO ${dbVars.userPrefix}_avatar_history (avatar_id, created_at, time)
|
||||
VALUES (@avatar_id, @created_at, 0)
|
||||
ON CONFLICT(avatar_id) DO UPDATE SET created_at = @created_at`,
|
||||
{
|
||||
'@avatar_id': avatarId,
|
||||
'@created_at': new Date().toJSON()
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getAvatarTimeSpent(avatarId) {
|
||||
var ref = {
|
||||
timeSpent: 0,
|
||||
avatarId
|
||||
};
|
||||
await sqliteService.execute(
|
||||
(row) => {
|
||||
ref.timeSpent = row[0];
|
||||
},
|
||||
`SELECT time FROM ${dbVars.userPrefix}_avatar_history WHERE avatar_id = @avatarId`,
|
||||
{
|
||||
'@avatarId': avatarId
|
||||
}
|
||||
);
|
||||
|
||||
return ref;
|
||||
},
|
||||
|
||||
addAvatarTimeSpent(avatarId, timeSpent) {
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE ${dbVars.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[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 ${dbVars.userPrefix}_avatar_history INNER JOIN cache_avatar ON cache_avatar.id = ${dbVars.userPrefix}_avatar_history.avatar_id WHERE author_id != "${currentUserId}" ORDER BY ${dbVars.userPrefix}_avatar_history.created_at DESC LIMIT ${limit}`);
|
||||
return data;
|
||||
},
|
||||
|
||||
async getCachedAvatarById(id) {
|
||||
var data = null;
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
data = {
|
||||
id: dbRow[0],
|
||||
// added_at: dbRow[1],
|
||||
authorId: dbRow[2],
|
||||
authorName: dbRow[3],
|
||||
created_at: dbRow[4],
|
||||
description: dbRow[5],
|
||||
imageUrl: dbRow[6],
|
||||
name: dbRow[7],
|
||||
releaseStatus: dbRow[8],
|
||||
thumbnailImageUrl: dbRow[9],
|
||||
updated_at: dbRow[10],
|
||||
version: dbRow[11]
|
||||
};
|
||||
},
|
||||
`SELECT * FROM cache_avatar WHERE id = @id`,
|
||||
{
|
||||
'@id': id
|
||||
}
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
clearAvatarHistory() {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_avatar_history`
|
||||
);
|
||||
sqliteService.executeNonQuery('DELETE FROM cache_avatar');
|
||||
},
|
||||
|
||||
addAvatarToFavorites(avatarId, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
'INSERT OR REPLACE INTO favorite_avatar (avatar_id, group_name, created_at) VALUES (@avatar_id, @group_name, @created_at)',
|
||||
{
|
||||
'@avatar_id': avatarId,
|
||||
'@group_name': groupName,
|
||||
'@created_at': new Date().toJSON()
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
renameAvatarFavoriteGroup(newGroupName, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE favorite_avatar SET group_name = @new_group_name WHERE group_name = @group_name`,
|
||||
{
|
||||
'@new_group_name': newGroupName,
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteAvatarFavoriteGroup(groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM favorite_avatar WHERE group_name = @group_name`,
|
||||
{
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
removeAvatarFromFavorites(avatarId, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM favorite_avatar WHERE avatar_id = @avatar_id AND group_name = @group_name`,
|
||||
{
|
||||
'@avatar_id': avatarId,
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getAvatarFavorites() {
|
||||
var data = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
created_at: dbRow[1],
|
||||
avatarId: dbRow[2],
|
||||
groupName: dbRow[3]
|
||||
};
|
||||
data.push(row);
|
||||
}, 'SELECT * FROM favorite_avatar');
|
||||
return data;
|
||||
},
|
||||
|
||||
removeAvatarFromCache(avatarId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM cache_avatar WHERE id = @avatar_id`,
|
||||
{
|
||||
'@avatar_id': avatarId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getAvatarCache() {
|
||||
var data = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
id: dbRow[0],
|
||||
// added_at: dbRow[1],
|
||||
authorId: dbRow[2],
|
||||
authorName: dbRow[3],
|
||||
created_at: dbRow[4],
|
||||
description: dbRow[5],
|
||||
imageUrl: dbRow[6],
|
||||
name: dbRow[7],
|
||||
releaseStatus: dbRow[8],
|
||||
thumbnailImageUrl: dbRow[9],
|
||||
updated_at: dbRow[10],
|
||||
version: dbRow[11]
|
||||
};
|
||||
data.push(row);
|
||||
}, 'SELECT * FROM cache_avatar');
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
export { avatarFavorites };
|
||||
@@ -0,0 +1,434 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const feed = {
|
||||
async getFeedDatabase() {
|
||||
var feedDatabase = [];
|
||||
var date = new Date();
|
||||
date.setDate(date.getDate() - 1); // 24 hour limit
|
||||
var dateOffset = date.toJSON();
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'GPS',
|
||||
location: dbRow[4],
|
||||
worldName: dbRow[5],
|
||||
previousLocation: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_gps WHERE created_at >= date('${dateOffset}') ORDER BY id DESC`);
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Status',
|
||||
status: dbRow[4],
|
||||
statusDescription: dbRow[5],
|
||||
previousStatus: dbRow[6],
|
||||
previousStatusDescription: dbRow[7]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_status WHERE created_at >= date('${dateOffset}') ORDER BY id DESC`);
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Bio',
|
||||
bio: dbRow[4],
|
||||
previousBio: dbRow[5]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_bio WHERE created_at >= date('${dateOffset}') ORDER BY id DESC`);
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Avatar',
|
||||
ownerId: dbRow[4],
|
||||
avatarName: dbRow[5],
|
||||
currentAvatarImageUrl: dbRow[6],
|
||||
currentAvatarThumbnailImageUrl: dbRow[7],
|
||||
previousCurrentAvatarImageUrl: dbRow[8],
|
||||
previousCurrentAvatarThumbnailImageUrl: dbRow[9]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_avatar WHERE created_at >= date('${dateOffset}') ORDER BY id DESC`);
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: dbRow[4],
|
||||
location: dbRow[5],
|
||||
worldName: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_online_offline WHERE created_at >= date('${dateOffset}') ORDER BY id DESC`);
|
||||
var compareByCreatedAt = function (a, b) {
|
||||
var A = a.created_at;
|
||||
var B = b.created_at;
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
feedDatabase.sort(compareByCreatedAt);
|
||||
return feedDatabase;
|
||||
},
|
||||
|
||||
addGPSToDatabase(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_feed_gps (created_at, user_id, display_name, location, world_name, previous_location, time, group_name) VALUES (@created_at, @user_id, @display_name, @location, @world_name, @previous_location, @time, @group_name)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@location': entry.location,
|
||||
'@world_name': entry.worldName,
|
||||
'@previous_location': entry.previousLocation,
|
||||
'@time': entry.time,
|
||||
'@group_name': entry.groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addStatusToDatabase(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_feed_status (created_at, user_id, display_name, status, status_description, previous_status, previous_status_description) VALUES (@created_at, @user_id, @display_name, @status, @status_description, @previous_status, @previous_status_description)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@status': entry.status,
|
||||
'@status_description': entry.statusDescription,
|
||||
'@previous_status': entry.previousStatus,
|
||||
'@previous_status_description': entry.previousStatusDescription
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addBioToDatabase(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_feed_bio (created_at, user_id, display_name, bio, previous_bio) VALUES (@created_at, @user_id, @display_name, @bio, @previous_bio)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@bio': entry.bio,
|
||||
'@previous_bio': entry.previousBio
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addAvatarToDatabase(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_feed_avatar (created_at, user_id, display_name, owner_id, avatar_name, current_avatar_image_url, current_avatar_thumbnail_image_url, previous_current_avatar_image_url, previous_current_avatar_thumbnail_image_url) VALUES (@created_at, @user_id, @display_name, @owner_id, @avatar_name, @current_avatar_image_url, @current_avatar_thumbnail_image_url, @previous_current_avatar_image_url, @previous_current_avatar_thumbnail_image_url)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@owner_id': entry.ownerId,
|
||||
'@avatar_name': entry.avatarName,
|
||||
'@current_avatar_image_url': entry.currentAvatarImageUrl,
|
||||
'@current_avatar_thumbnail_image_url':
|
||||
entry.currentAvatarThumbnailImageUrl,
|
||||
'@previous_current_avatar_image_url':
|
||||
entry.previousCurrentAvatarImageUrl,
|
||||
'@previous_current_avatar_thumbnail_image_url':
|
||||
entry.previousCurrentAvatarThumbnailImageUrl
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addOnlineOfflineToDatabase(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_feed_online_offline (created_at, user_id, display_name, type, location, world_name, time, group_name) VALUES (@created_at, @user_id, @display_name, @type, @location, @world_name, @time, @group_name)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@type': entry.type,
|
||||
'@location': entry.location,
|
||||
'@world_name': entry.worldName,
|
||||
'@time': entry.time,
|
||||
'@group_name': entry.groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async lookupFeedDatabase(search, filters, vipList) {
|
||||
var search = search.replaceAll("'", "''");
|
||||
if (search.startsWith('wrld_') || search.startsWith('grp_')) {
|
||||
return this.getFeedByInstanceId(search, filters, vipList);
|
||||
}
|
||||
var vipQuery = '';
|
||||
if (vipList.length > 0) {
|
||||
vipQuery = 'AND user_id IN (';
|
||||
vipList.forEach((vip, i) => {
|
||||
vipQuery += `'${vip.replaceAll("'", "''")}'`;
|
||||
if (i < vipList.length - 1) {
|
||||
vipQuery += ', ';
|
||||
}
|
||||
});
|
||||
vipQuery += ')';
|
||||
}
|
||||
var gps = true;
|
||||
var status = true;
|
||||
var bio = true;
|
||||
var avatar = true;
|
||||
var online = true;
|
||||
var offline = true;
|
||||
var aviPublic = search.includes('public');
|
||||
var aviPrivate = search.includes('private');
|
||||
if (filters.length > 0) {
|
||||
gps = false;
|
||||
status = false;
|
||||
bio = false;
|
||||
avatar = false;
|
||||
online = false;
|
||||
offline = false;
|
||||
filters.forEach((filter) => {
|
||||
switch (filter) {
|
||||
case 'GPS':
|
||||
gps = true;
|
||||
break;
|
||||
case 'Status':
|
||||
status = true;
|
||||
break;
|
||||
case 'Bio':
|
||||
bio = true;
|
||||
break;
|
||||
case 'Avatar':
|
||||
avatar = true;
|
||||
break;
|
||||
case 'Online':
|
||||
online = true;
|
||||
break;
|
||||
case 'Offline':
|
||||
offline = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
var feedDatabase = [];
|
||||
if (gps) {
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'GPS',
|
||||
location: dbRow[4],
|
||||
worldName: dbRow[5],
|
||||
previousLocation: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_gps WHERE (display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
if (status) {
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Status',
|
||||
status: dbRow[4],
|
||||
statusDescription: dbRow[5],
|
||||
previousStatus: dbRow[6],
|
||||
previousStatusDescription: dbRow[7]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_status WHERE (display_name LIKE '%${search}%' OR status LIKE '%${search}%' OR status_description LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
if (bio) {
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Bio',
|
||||
bio: dbRow[4],
|
||||
previousBio: dbRow[5]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_bio WHERE (display_name LIKE '%${search}%' OR bio LIKE '%${search}%') ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
if (avatar) {
|
||||
var query = '';
|
||||
if (aviPrivate) {
|
||||
query = 'OR user_id = owner_id';
|
||||
} else if (aviPublic) {
|
||||
query = 'OR user_id != owner_id';
|
||||
}
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'Avatar',
|
||||
ownerId: dbRow[4],
|
||||
avatarName: dbRow[5],
|
||||
currentAvatarImageUrl: dbRow[6],
|
||||
currentAvatarThumbnailImageUrl: dbRow[7],
|
||||
previousCurrentAvatarImageUrl: dbRow[8],
|
||||
previousCurrentAvatarThumbnailImageUrl: dbRow[9]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_avatar WHERE ((display_name LIKE '%${search}%' OR avatar_name LIKE '%${search}%') ${query}) ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
if (online || offline) {
|
||||
var query = '';
|
||||
if (!online || !offline) {
|
||||
if (online) {
|
||||
query = "AND type = 'Online'";
|
||||
} else if (offline) {
|
||||
query = "AND type = 'Offline'";
|
||||
}
|
||||
}
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: dbRow[4],
|
||||
location: dbRow[5],
|
||||
worldName: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_online_offline WHERE ((display_name LIKE '%${search}%' OR world_name LIKE '%${search}%' OR group_name LIKE '%${search}%') ${query}) ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
var compareByCreatedAt = function (a, b) {
|
||||
var A = a.created_at;
|
||||
var B = b.created_at;
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
feedDatabase.sort(compareByCreatedAt);
|
||||
feedDatabase.splice(0, feedDatabase.length - dbVars.maxTableSize);
|
||||
return feedDatabase;
|
||||
},
|
||||
|
||||
async getFeedByInstanceId(instanceId, filters, vipList) {
|
||||
var feedDatabase = [];
|
||||
var vipQuery = '';
|
||||
if (vipList.length > 0) {
|
||||
vipQuery = 'AND user_id IN (';
|
||||
vipList.forEach((vip, i) => {
|
||||
vipQuery += `'${vip.replaceAll("'", "''")}'`;
|
||||
if (i < vipList.length - 1) {
|
||||
vipQuery += ', ';
|
||||
}
|
||||
});
|
||||
vipQuery += ')';
|
||||
}
|
||||
var gps = true;
|
||||
var online = true;
|
||||
var offline = true;
|
||||
if (filters.length > 0) {
|
||||
gps = false;
|
||||
online = false;
|
||||
offline = false;
|
||||
filters.forEach((filter) => {
|
||||
switch (filter) {
|
||||
case 'GPS':
|
||||
gps = true;
|
||||
break;
|
||||
case 'Online':
|
||||
online = true;
|
||||
break;
|
||||
case 'Offline':
|
||||
offline = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (gps) {
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: 'GPS',
|
||||
location: dbRow[4],
|
||||
worldName: dbRow[5],
|
||||
previousLocation: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_gps WHERE location LIKE '%${instanceId}%' ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
if (online || offline) {
|
||||
var query = '';
|
||||
if (!online || !offline) {
|
||||
if (online) {
|
||||
query = "AND type = 'Online'";
|
||||
} else if (offline) {
|
||||
query = "AND type = 'Offline'";
|
||||
}
|
||||
}
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
userId: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
type: dbRow[4],
|
||||
location: dbRow[5],
|
||||
worldName: dbRow[6],
|
||||
time: dbRow[7],
|
||||
groupName: dbRow[8]
|
||||
};
|
||||
feedDatabase.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_feed_online_offline WHERE (location LIKE '%${instanceId}%' ${query}) ${vipQuery} ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
}
|
||||
var compareByCreatedAt = function (a, b) {
|
||||
var A = a.created_at;
|
||||
var B = b.created_at;
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
feedDatabase.sort(compareByCreatedAt);
|
||||
feedDatabase.splice(0, feedDatabase.length - dbVars.maxTableSize);
|
||||
return feedDatabase;
|
||||
}
|
||||
};
|
||||
|
||||
export { feed };
|
||||
@@ -0,0 +1,64 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const friendLogCurrent = {
|
||||
async getFriendLogCurrent() {
|
||||
var friendLogCurrent = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
userId: dbRow[0],
|
||||
displayName: dbRow[1],
|
||||
trustLevel: dbRow[2],
|
||||
friendNumber: dbRow[3]
|
||||
};
|
||||
friendLogCurrent.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_friend_log_current`);
|
||||
return friendLogCurrent;
|
||||
},
|
||||
|
||||
setFriendLogCurrent(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO ${dbVars.userPrefix}_friend_log_current (user_id, display_name, trust_level, friend_number) VALUES (@user_id, @display_name, @trust_level, @friend_number)`,
|
||||
{
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@trust_level': entry.trustLevel,
|
||||
'@friend_number': entry.friendNumber
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
setFriendLogCurrentArray(inputData) {
|
||||
if (inputData.length === 0) {
|
||||
return;
|
||||
}
|
||||
var sqlValues = '';
|
||||
var items = ['userId', 'displayName', 'trustLevel'];
|
||||
for (var line of inputData) {
|
||||
var field = {};
|
||||
for (var item of items) {
|
||||
if (typeof line[item] === 'string') {
|
||||
field[item] = line[item].replace(/'/g, "''");
|
||||
} else {
|
||||
field[item] = '';
|
||||
}
|
||||
}
|
||||
sqlValues += `('${field.userId}', '${field.displayName}', '${field.trustLevel}', ${line.friendNumber}), `;
|
||||
}
|
||||
sqlValues = sqlValues.slice(0, -2);
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO ${dbVars.userPrefix}_friend_log_current (user_id, display_name, trust_level, friend_number) VALUES ${sqlValues}`
|
||||
);
|
||||
},
|
||||
|
||||
deleteFriendLogCurrent(userId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_friend_log_current WHERE user_id = @user_id`,
|
||||
{
|
||||
'@user_id': userId
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { friendLogCurrent };
|
||||
@@ -0,0 +1,95 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const friendLogHistory = {
|
||||
async getFriendLogHistory() {
|
||||
var friendLogHistory = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
type: dbRow[2],
|
||||
userId: dbRow[3],
|
||||
displayName: dbRow[4],
|
||||
friendNumber: dbRow[8]
|
||||
};
|
||||
if (row.type === 'DisplayName') {
|
||||
row.previousDisplayName = dbRow[5];
|
||||
} else if (row.type === 'TrustLevel') {
|
||||
row.trustLevel = dbRow[6];
|
||||
row.previousTrustLevel = dbRow[7];
|
||||
}
|
||||
friendLogHistory.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_friend_log_history`);
|
||||
return friendLogHistory;
|
||||
},
|
||||
|
||||
addFriendLogHistory(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_friend_log_history (created_at, type, user_id, display_name, previous_display_name, trust_level, previous_trust_level, friend_number) VALUES (@created_at, @type, @user_id, @display_name, @previous_display_name, @trust_level, @previous_trust_level, @friend_number)`,
|
||||
{
|
||||
'@created_at': entry.created_at,
|
||||
'@type': entry.type,
|
||||
'@user_id': entry.userId,
|
||||
'@display_name': entry.displayName,
|
||||
'@previous_display_name': entry.previousDisplayName,
|
||||
'@trust_level': entry.trustLevel,
|
||||
'@previous_trust_level': entry.previousTrustLevel,
|
||||
'@friend_number': entry.friendNumber
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addFriendLogHistoryArray(inputData) {
|
||||
if (inputData.length === 0) {
|
||||
return;
|
||||
}
|
||||
var sqlValues = '';
|
||||
var items = [
|
||||
'created_at',
|
||||
'type',
|
||||
'userId',
|
||||
'displayName',
|
||||
'previousDisplayName',
|
||||
'trustLevel',
|
||||
'previousTrustLevel',
|
||||
'friendNumber'
|
||||
];
|
||||
for (var i = 0; i < inputData.length; ++i) {
|
||||
var line = inputData[i];
|
||||
sqlValues += '(';
|
||||
for (var k = 0; k < items.length; ++k) {
|
||||
var item = items[k];
|
||||
var field = '';
|
||||
if (typeof line[item] === 'string') {
|
||||
field = `'${line[item].replace(/'/g, "''")}'`;
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
sqlValues += field;
|
||||
if (k < items.length - 1) {
|
||||
sqlValues += ', ';
|
||||
}
|
||||
}
|
||||
sqlValues += ')';
|
||||
if (i < inputData.length - 1) {
|
||||
sqlValues += ', ';
|
||||
}
|
||||
// sqlValues `('${line.created_at}', '${line.type}', '${line.userId}', '${line.displayName}', '${line.previousDisplayName}', '${line.trustLevel}', '${line.previousTrustLevel}'), `
|
||||
}
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_friend_log_history (created_at, type, user_id, display_name, previous_display_name, trust_level, previous_trust_level, friend_number) VALUES ${sqlValues}`
|
||||
);
|
||||
},
|
||||
|
||||
deleteFriendLogHistory(rowId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_friend_log_history WHERE id = @row_id`,
|
||||
{
|
||||
'@row_id': rowId
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { friendLogHistory };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const memos = {
|
||||
// user memos
|
||||
|
||||
async getUserMemo(userId) {
|
||||
var row = {};
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
row = {
|
||||
userId: dbRow[0],
|
||||
editedAt: dbRow[1],
|
||||
memo: dbRow[2]
|
||||
};
|
||||
},
|
||||
`SELECT * FROM memos WHERE user_id = @user_id`,
|
||||
{
|
||||
'@user_id': userId
|
||||
}
|
||||
);
|
||||
return row;
|
||||
},
|
||||
|
||||
async getAllUserMemos() {
|
||||
var memos = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
userId: dbRow[0],
|
||||
memo: dbRow[1]
|
||||
};
|
||||
memos.push(row);
|
||||
}, 'SELECT user_id, memo FROM memos');
|
||||
return memos;
|
||||
},
|
||||
|
||||
async setUserMemo(entry) {
|
||||
await sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO memos (user_id, edited_at, memo) VALUES (@user_id, @edited_at, @memo)`,
|
||||
{
|
||||
'@user_id': entry.userId,
|
||||
'@edited_at': entry.editedAt,
|
||||
'@memo': entry.memo
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async deleteUserMemo(userId) {
|
||||
await sqliteService.executeNonQuery(
|
||||
`DELETE FROM memos WHERE user_id = @user_id`,
|
||||
{
|
||||
'@user_id': userId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// world memos
|
||||
|
||||
async getWorldMemo(worldId) {
|
||||
var row = {};
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
row = {
|
||||
worldId: dbRow[0],
|
||||
editedAt: dbRow[1],
|
||||
memo: dbRow[2]
|
||||
};
|
||||
},
|
||||
`SELECT * FROM world_memos WHERE world_id = @world_id`,
|
||||
{
|
||||
'@world_id': worldId
|
||||
}
|
||||
);
|
||||
return row;
|
||||
},
|
||||
|
||||
setWorldMemo(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO world_memos (world_id, edited_at, memo) VALUES (@world_id, @edited_at, @memo)`,
|
||||
{
|
||||
'@world_id': entry.worldId,
|
||||
'@edited_at': entry.editedAt,
|
||||
'@memo': entry.memo
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteWorldMemo(worldId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM world_memos WHERE world_id = @world_id`,
|
||||
{
|
||||
'@world_id': worldId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Avatar memos
|
||||
|
||||
async getAvatarMemoDB(avatarId) {
|
||||
var row = {};
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
row = {
|
||||
avatarId: dbRow[0],
|
||||
editedAt: dbRow[1],
|
||||
memo: dbRow[2]
|
||||
};
|
||||
},
|
||||
`SELECT * FROM avatar_memos WHERE avatar_id = @avatar_id`,
|
||||
{
|
||||
'@avatar_id': avatarId
|
||||
}
|
||||
);
|
||||
return row;
|
||||
},
|
||||
|
||||
setAvatarMemo(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO avatar_memos (avatar_id, edited_at, memo) VALUES (@avatar_id, @edited_at, @memo)`,
|
||||
{
|
||||
'@avatar_id': entry.avatarId,
|
||||
'@edited_at': entry.editedAt,
|
||||
'@memo': entry.memo
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteAvatarMemo(avatarId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM avatar_memos WHERE avatar_id = @avatar_id`,
|
||||
{
|
||||
'@avatar_id': avatarId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// user notes
|
||||
|
||||
async addUserNote(note) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO ${dbVars.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 ${dbVars.userPrefix}_notes`);
|
||||
return data;
|
||||
},
|
||||
|
||||
async deleteUserNote(userId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_notes WHERE user_id = @userId`,
|
||||
{
|
||||
'@userId': userId
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { memos };
|
||||
@@ -0,0 +1,64 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const moderation = {
|
||||
async getModeration(userId) {
|
||||
var row = {};
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
var block = false;
|
||||
var mute = false;
|
||||
if (dbRow[3] === 1) {
|
||||
block = true;
|
||||
}
|
||||
if (dbRow[4] === 1) {
|
||||
mute = true;
|
||||
}
|
||||
row = {
|
||||
userId: dbRow[0],
|
||||
updatedAt: dbRow[1],
|
||||
displayName: dbRow[2],
|
||||
block,
|
||||
mute
|
||||
};
|
||||
},
|
||||
`SELECT * FROM ${dbVars.userPrefix}_moderation WHERE user_id = @userId`,
|
||||
{
|
||||
'@userId': userId
|
||||
}
|
||||
);
|
||||
return row;
|
||||
},
|
||||
|
||||
setModeration(entry) {
|
||||
var block = 0;
|
||||
var mute = 0;
|
||||
if (entry.block) {
|
||||
block = 1;
|
||||
}
|
||||
if (entry.mute) {
|
||||
mute = 1;
|
||||
}
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO ${dbVars.userPrefix}_moderation (user_id, updated_at, display_name, block, mute) VALUES (@user_id, @updated_at, @display_name, @block, @mute)`,
|
||||
{
|
||||
'@user_id': entry.userId,
|
||||
'@updated_at': entry.updatedAt,
|
||||
'@display_name': entry.displayName,
|
||||
'@block': block,
|
||||
'@mute': mute
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteModeration(userId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_moderation WHERE user_id = @user_id`,
|
||||
{
|
||||
'@user_id': userId
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { moderation };
|
||||
@@ -0,0 +1,110 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const notifications = {
|
||||
async getNotifications() {
|
||||
var notifications = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
id: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
type: dbRow[2],
|
||||
senderUserId: dbRow[3],
|
||||
senderUsername: dbRow[4],
|
||||
receiverUserId: dbRow[5],
|
||||
message: dbRow[6],
|
||||
details: {
|
||||
worldId: dbRow[7],
|
||||
worldName: dbRow[8],
|
||||
imageUrl: dbRow[9],
|
||||
inviteMessage: dbRow[10],
|
||||
requestMessage: dbRow[11],
|
||||
responseMessage: dbRow[12]
|
||||
}
|
||||
};
|
||||
row.$isExpired = false;
|
||||
if (dbRow[13] === 1) {
|
||||
row.$isExpired = true;
|
||||
}
|
||||
notifications.unshift(row);
|
||||
}, `SELECT * FROM ${dbVars.userPrefix}_notifications ORDER BY created_at DESC LIMIT ${dbVars.maxTableSize}`);
|
||||
return notifications;
|
||||
},
|
||||
|
||||
addNotificationToDatabase(row) {
|
||||
var entry = {
|
||||
id: '',
|
||||
created_at: '',
|
||||
type: '',
|
||||
senderUserId: '',
|
||||
senderUsername: '',
|
||||
receiverUserId: '',
|
||||
message: '',
|
||||
...row,
|
||||
details: {
|
||||
worldId: '',
|
||||
worldName: '',
|
||||
imageUrl: '',
|
||||
inviteMessage: '',
|
||||
requestMessage: '',
|
||||
responseMessage: '',
|
||||
...row.details
|
||||
}
|
||||
};
|
||||
if (entry.imageUrl && !entry.details.imageUrl) {
|
||||
entry.details.imageUrl = entry.imageUrl;
|
||||
}
|
||||
var expired = 0;
|
||||
if (row.$isExpired) {
|
||||
expired = 1;
|
||||
}
|
||||
if (!entry.created_at || !entry.type || !entry.id) {
|
||||
console.error('Notification is missing required field', entry);
|
||||
throw new Error('Notification is missing required field');
|
||||
}
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR IGNORE INTO ${dbVars.userPrefix}_notifications (id, created_at, type, sender_user_id, sender_username, receiver_user_id, message, world_id, world_name, image_url, invite_message, request_message, response_message, expired) VALUES (@id, @created_at, @type, @sender_user_id, @sender_username, @receiver_user_id, @message, @world_id, @world_name, @image_url, @invite_message, @request_message, @response_message, @expired)`,
|
||||
{
|
||||
'@id': entry.id,
|
||||
'@created_at': entry.created_at,
|
||||
'@type': entry.type,
|
||||
'@sender_user_id': entry.senderUserId,
|
||||
'@sender_username': entry.senderUsername,
|
||||
'@receiver_user_id': entry.receiverUserId,
|
||||
'@message': entry.message,
|
||||
'@world_id': entry.details.worldId,
|
||||
'@world_name': entry.details.worldName,
|
||||
'@image_url': entry.details.imageUrl,
|
||||
'@invite_message': entry.details.inviteMessage,
|
||||
'@request_message': entry.details.requestMessage,
|
||||
'@response_message': entry.details.responseMessage,
|
||||
'@expired': expired
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteNotification(rowId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_notifications WHERE id = @row_id`,
|
||||
{
|
||||
'@row_id': rowId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
updateNotificationExpired(entry) {
|
||||
var expired = 0;
|
||||
if (entry.$isExpired) {
|
||||
expired = 1;
|
||||
}
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE ${dbVars.userPrefix}_notifications SET expired = @expired WHERE id = @id`,
|
||||
{
|
||||
'@id': entry.id,
|
||||
'@expired': expired
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export { notifications };
|
||||
@@ -0,0 +1,79 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const tableAlter = {
|
||||
async upgradeDatabaseVersion() {
|
||||
// var version = 0;
|
||||
// await sqliteService.execute((dbRow) => {
|
||||
// version = dbRow[0];
|
||||
// }, 'PRAGMA user_version');
|
||||
// if (version === 0) {
|
||||
await this.updateTableForGroupNames();
|
||||
await this.addFriendLogFriendNumber();
|
||||
await this.updateTableForAvatarHistory();
|
||||
// }
|
||||
// await sqliteService.executeNonQuery('PRAGMA user_version = 1');
|
||||
},
|
||||
|
||||
async updateTableForGroupNames() {
|
||||
var tables = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
tables.push(dbRow[0]);
|
||||
}, `SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE '%_feed_gps' OR name LIKE '%_feed_online_offline' OR name = 'gamelog_location'`);
|
||||
for (var tableName of tables) {
|
||||
try {
|
||||
await sqliteService.executeNonQuery(
|
||||
`ALTER TABLE ${tableName} ADD group_name TEXT DEFAULT ''`
|
||||
);
|
||||
} catch (e) {
|
||||
e = e.toString();
|
||||
if (e.indexOf('duplicate column name') === -1) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fix gamelog_location column typo
|
||||
try {
|
||||
await sqliteService.executeNonQuery(
|
||||
`ALTER TABLE gamelog_location DROP COLUMN groupName`
|
||||
);
|
||||
} catch (e) {
|
||||
e = e.toString();
|
||||
if (e.indexOf('no such column') === -1) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async addFriendLogFriendNumber() {
|
||||
var tables = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
tables.push(dbRow[0]);
|
||||
}, `SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE '%_friend_log_current' OR name LIKE '%_friend_log_history'`);
|
||||
for (var tableName of tables) {
|
||||
try {
|
||||
await sqliteService.executeNonQuery(
|
||||
`ALTER TABLE ${tableName} ADD friend_number INTEGER DEFAULT 0`
|
||||
);
|
||||
} catch (e) {
|
||||
e = e.toString();
|
||||
if (e.indexOf('duplicate column name') === -1) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async updateTableForAvatarHistory() {
|
||||
await sqliteService.execute((dbRow) => {
|
||||
const columnExists = dbRow.some((row) => row.name === 'time');
|
||||
if (!columnExists) {
|
||||
sqliteService.executeNonQuery(
|
||||
`ALTER TABLE ${dbVars.userPrefix}_avatar_history ADD time INTEGER DEFAULT 0`
|
||||
);
|
||||
}
|
||||
}, `PRAGMA table_info(${dbVars.userPrefix}_avatar_history);`);
|
||||
}
|
||||
};
|
||||
|
||||
export { tableAlter };
|
||||
@@ -0,0 +1,156 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const tableFixes = {
|
||||
async cleanLegendFromFriendLog() {
|
||||
await sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_friend_log_history
|
||||
WHERE type = 'TrustLevel' AND created_at > '2022-05-04T01:00:00.000Z'
|
||||
AND ((trust_level = 'Veteran User' AND previous_trust_level = 'Trusted User') OR (trust_level = 'Trusted User' AND previous_trust_level = 'Veteran User'))`
|
||||
);
|
||||
},
|
||||
|
||||
async fixGameLogTraveling() {
|
||||
var travelingList = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
type: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
location: dbRow[4],
|
||||
userId: dbRow[5],
|
||||
time: dbRow[6]
|
||||
};
|
||||
travelingList.unshift(row);
|
||||
}, 'SELECT * FROM gamelog_join_leave WHERE type = "OnPlayerLeft" AND location = "traveling"');
|
||||
travelingList.forEach(async (travelingEntry) => {
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
var onPlayingJoin = {
|
||||
rowId: dbRow[0],
|
||||
created_at: dbRow[1],
|
||||
type: dbRow[2],
|
||||
displayName: dbRow[3],
|
||||
location: dbRow[4],
|
||||
userId: dbRow[5],
|
||||
time: dbRow[6]
|
||||
};
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE gamelog_join_leave SET location = @location WHERE id = @rowId`,
|
||||
{
|
||||
'@rowId': travelingEntry.rowId,
|
||||
'@location': onPlayingJoin.location
|
||||
}
|
||||
);
|
||||
},
|
||||
'SELECT * FROM gamelog_join_leave WHERE type = "OnPlayerJoined" AND display_name = @displayName AND created_at <= @created_at ORDER BY created_at DESC LIMIT 1',
|
||||
{
|
||||
'@displayName': travelingEntry.displayName,
|
||||
'@created_at': travelingEntry.created_at
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
async fixNegativeGPS() {
|
||||
var gpsTables = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
gpsTables.push(dbRow[0]);
|
||||
}, `SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE '%_gps'`);
|
||||
gpsTables.forEach((tableName) => {
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE ${tableName} SET time = 0 WHERE time < 0`
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
async getBrokenLeaveEntries() {
|
||||
var instances = await this.getGameLogInstancesTime();
|
||||
var badEntries = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
if (typeof dbRow[1] === 'number') {
|
||||
var ref = instances.get(dbRow[0]);
|
||||
if (typeof ref !== 'undefined' && dbRow[1] > ref) {
|
||||
badEntries.push(dbRow[2]);
|
||||
}
|
||||
}
|
||||
}, `SELECT location, time, id FROM gamelog_join_leave WHERE type = 'OnPlayerLeft' AND time > 0`);
|
||||
return badEntries;
|
||||
},
|
||||
|
||||
async fixBrokenLeaveEntries() {
|
||||
var badEntries = await this.getBrokenLeaveEntries();
|
||||
var badEntriesList = '';
|
||||
var count = badEntries.length;
|
||||
badEntries.forEach((entry) => {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
badEntriesList = badEntriesList.concat(entry);
|
||||
} else {
|
||||
badEntriesList = badEntriesList.concat(`${entry}, `);
|
||||
}
|
||||
});
|
||||
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE gamelog_join_leave SET time = 0 WHERE id IN (${badEntriesList})`
|
||||
);
|
||||
},
|
||||
|
||||
async fixBrokenGroupInvites() {
|
||||
var notificationTables = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
notificationTables.push(dbRow[0]);
|
||||
}, `SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE '%_notifications'`);
|
||||
notificationTables.forEach((tableName) => {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${tableName} WHERE type LIKE '%.%'`
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
async fixBrokenNotifications() {
|
||||
await sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_notifications WHERE (created_at is null or created_at = '')`
|
||||
);
|
||||
},
|
||||
|
||||
async fixBrokenGroupChange() {
|
||||
await sqliteService.executeNonQuery(
|
||||
`DELETE FROM ${dbVars.userPrefix}_notifications WHERE type = 'groupChange' AND created_at < '2024-04-23T03:00:00.000Z'`
|
||||
);
|
||||
},
|
||||
|
||||
async fixCancelFriendRequestTypo() {
|
||||
await sqliteService.executeNonQuery(
|
||||
`UPDATE ${dbVars.userPrefix}_friend_log_history SET type = 'CancelFriendRequest' WHERE type = 'CancelFriendRequst'`
|
||||
);
|
||||
},
|
||||
|
||||
async getBrokenGameLogDisplayNames() {
|
||||
var badEntries = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
badEntries.push({
|
||||
id: dbRow[0],
|
||||
displayName: dbRow[1]
|
||||
});
|
||||
}, 'SELECT id, display_name FROM gamelog_join_leave WHERE display_name LIKE "% (%"');
|
||||
return badEntries;
|
||||
},
|
||||
|
||||
async fixBrokenGameLogDisplayNames() {
|
||||
var badEntries = await this.getBrokenGameLogDisplayNames();
|
||||
badEntries.forEach((entry) => {
|
||||
var newDisplayName = entry.displayName.split(' (')[0];
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE gamelog_join_leave SET display_name = @new_display_name WHERE id = @id`,
|
||||
{
|
||||
'@new_display_name': newDisplayName,
|
||||
'@id': entry.id
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { tableFixes };
|
||||
@@ -0,0 +1,126 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
import { dbVars } from '../database';
|
||||
|
||||
const tableSize = {
|
||||
async getMaxFriendLogNumber() {
|
||||
var friendNumber = 0;
|
||||
await sqliteService.execute((dbRow) => {
|
||||
friendNumber = dbRow[0];
|
||||
}, `SELECT MAX(friend_number) FROM ${dbVars.userPrefix}_friend_log_current`);
|
||||
return friendNumber;
|
||||
},
|
||||
|
||||
async getGpsTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_feed_gps`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getStatusTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_feed_status`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getBioTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_feed_bio`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getAvatarTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_feed_avatar`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getOnlineOfflineTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_feed_online_offline`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getFriendLogHistoryTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_friend_log_history`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getNotificationTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM ${dbVars.userPrefix}_notifications`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getLocationTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_location`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getJoinLeaveTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_join_leave`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getPortalSpawnTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_portal_spawn`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getVideoPlayTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_video_play`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getResourceLoadTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_resource_load`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getEventTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_event`);
|
||||
return size;
|
||||
},
|
||||
|
||||
async getExternalTableSize() {
|
||||
var size = 0;
|
||||
await sqliteService.execute((row) => {
|
||||
size = row[0];
|
||||
}, `SELECT COUNT(*) FROM gamelog_external`);
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
export { tableSize };
|
||||
@@ -0,0 +1,136 @@
|
||||
import sqliteService from '../sqlite.js';
|
||||
|
||||
const worldFavorites = {
|
||||
addWorldToCache(entry) {
|
||||
sqliteService.executeNonQuery(
|
||||
`INSERT OR REPLACE INTO cache_world (id, added_at, author_id, author_name, created_at, description, image_url, name, release_status, thumbnail_image_url, updated_at, version) VALUES (@id, @added_at, @author_id, @author_name, @created_at, @description, @image_url, @name, @release_status, @thumbnail_image_url, @updated_at, @version)`,
|
||||
{
|
||||
'@id': entry.id,
|
||||
'@added_at': new Date().toJSON(),
|
||||
'@author_id': entry.authorId,
|
||||
'@author_name': entry.authorName,
|
||||
'@created_at': entry.created_at,
|
||||
'@description': entry.description,
|
||||
'@image_url': entry.imageUrl,
|
||||
'@name': entry.name,
|
||||
'@release_status': entry.releaseStatus,
|
||||
'@thumbnail_image_url': entry.thumbnailImageUrl,
|
||||
'@updated_at': entry.updated_at,
|
||||
'@version': entry.version
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
addWorldToFavorites(worldId, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
'INSERT OR REPLACE INTO favorite_world (world_id, group_name, created_at) VALUES (@world_id, @group_name, @created_at)',
|
||||
{
|
||||
'@world_id': worldId,
|
||||
'@group_name': groupName,
|
||||
'@created_at': new Date().toJSON()
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
renameWorldFavoriteGroup(newGroupName, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`UPDATE favorite_world SET group_name = @new_group_name WHERE group_name = @group_name`,
|
||||
{
|
||||
'@new_group_name': newGroupName,
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteWorldFavoriteGroup(groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM favorite_world WHERE group_name = @group_name`,
|
||||
{
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
removeWorldFromFavorites(worldId, groupName) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM favorite_world WHERE world_id = @world_id AND group_name = @group_name`,
|
||||
{
|
||||
'@world_id': worldId,
|
||||
'@group_name': groupName
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getWorldFavorites() {
|
||||
var data = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
created_at: dbRow[1],
|
||||
worldId: dbRow[2],
|
||||
groupName: dbRow[3]
|
||||
};
|
||||
data.push(row);
|
||||
}, 'SELECT * FROM favorite_world');
|
||||
return data;
|
||||
},
|
||||
|
||||
removeWorldFromCache(worldId) {
|
||||
sqliteService.executeNonQuery(
|
||||
`DELETE FROM cache_world WHERE id = @world_id`,
|
||||
{
|
||||
'@world_id': worldId
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async getWorldCache() {
|
||||
var data = [];
|
||||
await sqliteService.execute((dbRow) => {
|
||||
var row = {
|
||||
id: dbRow[0],
|
||||
// added_at: dbRow[1],
|
||||
authorId: dbRow[2],
|
||||
authorName: dbRow[3],
|
||||
created_at: dbRow[4],
|
||||
description: dbRow[5],
|
||||
imageUrl: dbRow[6],
|
||||
name: dbRow[7],
|
||||
releaseStatus: dbRow[8],
|
||||
thumbnailImageUrl: dbRow[9],
|
||||
updated_at: dbRow[10],
|
||||
version: dbRow[11]
|
||||
};
|
||||
data.push(row);
|
||||
}, 'SELECT * FROM cache_world');
|
||||
return data;
|
||||
},
|
||||
|
||||
async getCachedWorldById(id) {
|
||||
var data = null;
|
||||
await sqliteService.execute(
|
||||
(dbRow) => {
|
||||
data = {
|
||||
id: dbRow[0],
|
||||
// added_at: dbRow[1],
|
||||
authorId: dbRow[2],
|
||||
authorName: dbRow[3],
|
||||
created_at: dbRow[4],
|
||||
description: dbRow[5],
|
||||
imageUrl: dbRow[6],
|
||||
name: dbRow[7],
|
||||
releaseStatus: dbRow[8],
|
||||
thumbnailImageUrl: dbRow[9],
|
||||
updated_at: dbRow[10],
|
||||
version: dbRow[11]
|
||||
};
|
||||
},
|
||||
`SELECT * FROM cache_world WHERE id = @id`,
|
||||
{
|
||||
'@id': id
|
||||
}
|
||||
);
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
export { worldFavorites };
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let VRCXStorage = {};
|
||||
|
||||
export default class {
|
||||
constructor(_VRCXStorage) {
|
||||
VRCXStorage = _VRCXStorage;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
VRCXStorage.GetArray = async function (key) {
|
||||
try {
|
||||
var array = JSON.parse(await this.Get(key));
|
||||
if (Array.isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
VRCXStorage.SetArray = function (key, value) {
|
||||
this.Set(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
VRCXStorage.GetObject = async function (key) {
|
||||
try {
|
||||
var object = JSON.parse(await this.Get(key));
|
||||
if (object === Object(object)) {
|
||||
return object;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
VRCXStorage.SetObject = function (key, value) {
|
||||
this.Set(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
workerTimers.setInterval(
|
||||
() => {
|
||||
VRCXStorage.Flush();
|
||||
},
|
||||
5 * 60 * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
import Noty from 'noty';
|
||||
import { $app } from '../app.js';
|
||||
import { t } from '../plugin';
|
||||
import { statusCodes } from '../shared/constants/api.js';
|
||||
import { escapeTag } from '../shared/utils';
|
||||
import {
|
||||
useAuthStore,
|
||||
useAvatarStore,
|
||||
useNotificationStore,
|
||||
useUpdateLoopStore,
|
||||
useUserStore
|
||||
} from '../stores';
|
||||
import { AppGlobal } from './appConfig.js';
|
||||
import webApiService from './webapi.js';
|
||||
import { watchState } from './watchState';
|
||||
|
||||
const pendingGetRequests = new Map();
|
||||
export let failedGetRequests = new Map();
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} endpoint
|
||||
* @param {RequestInit & { params?: any }} [options]
|
||||
* @returns {Promise<T>}
|
||||
*/
|
||||
export function request(endpoint, options) {
|
||||
const userStore = useUserStore();
|
||||
const avatarStore = useAvatarStore();
|
||||
const authStore = useAuthStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
const updateLoopStore = useUpdateLoopStore();
|
||||
if (
|
||||
!watchState.isLoggedIn &&
|
||||
endpoint.startsWith('/auth') &&
|
||||
endpoint !== 'config'
|
||||
) {
|
||||
throw `API request blocked while logged out: ${endpoint}`;
|
||||
}
|
||||
let req;
|
||||
const init = {
|
||||
url: `${AppGlobal.endpointDomain}/${endpoint}`,
|
||||
method: 'GET',
|
||||
...options
|
||||
};
|
||||
const { params } = init;
|
||||
if (init.method === 'GET') {
|
||||
// don't retry recent 404/403
|
||||
if (failedGetRequests.has(endpoint)) {
|
||||
const lastRun = failedGetRequests.get(endpoint);
|
||||
if (lastRun >= Date.now() - 900000) {
|
||||
// 15mins
|
||||
$throw(
|
||||
0,
|
||||
t('api.error.message.403_404_bailing_request'),
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
failedGetRequests.delete(endpoint);
|
||||
}
|
||||
// transform body to url
|
||||
if (params === Object(params)) {
|
||||
const url = new URL(init.url);
|
||||
const { searchParams } = url;
|
||||
for (const key in params) {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
init.url = url.toString();
|
||||
}
|
||||
// merge requests
|
||||
req = pendingGetRequests.get(init.url);
|
||||
if (typeof req !== 'undefined') {
|
||||
if (req.time >= Date.now() - 10000) {
|
||||
// 10s
|
||||
return req.req;
|
||||
}
|
||||
pendingGetRequests.delete(init.url);
|
||||
}
|
||||
} else if (
|
||||
init.uploadImage ||
|
||||
init.uploadFilePUT ||
|
||||
init.uploadImageLegacy
|
||||
) {
|
||||
// nothing
|
||||
} else {
|
||||
init.headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
...init.headers
|
||||
};
|
||||
init.body = params === Object(params) ? JSON.stringify(params) : '{}';
|
||||
}
|
||||
req = webApiService
|
||||
.execute(init)
|
||||
.catch((err) => {
|
||||
$throw(0, err, endpoint);
|
||||
})
|
||||
.then((response) => {
|
||||
if (
|
||||
!watchState.isLoggedIn &&
|
||||
endpoint.startsWith('/auth') &&
|
||||
endpoint !== 'config'
|
||||
) {
|
||||
throw `API request blocked while logged out: ${endpoint}`;
|
||||
}
|
||||
if (!response.data) {
|
||||
if (AppGlobal.debugWebRequests) {
|
||||
console.log(init, 'no data', response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
response.data = JSON.parse(response.data);
|
||||
if (AppGlobal.debugWebRequests) {
|
||||
console.log(init, 'parsed data', response.data);
|
||||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (response.status === 200) {
|
||||
$throw(
|
||||
0,
|
||||
t('api.error.message.invalid_json_response'),
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
if (
|
||||
response.status === 429 &&
|
||||
init.url.endsWith('/instances/groups')
|
||||
) {
|
||||
updateLoopStore.nextGroupInstanceRefresh = 120; // 1min
|
||||
$throw(429, t('api.status_code.429'), endpoint);
|
||||
}
|
||||
if (response.status === 504 || response.status === 502) {
|
||||
// ignore expected API errors
|
||||
$throw(response.status, response.data || '', endpoint);
|
||||
}
|
||||
$throw(
|
||||
response.status,
|
||||
response.data || response.statusText,
|
||||
endpoint
|
||||
);
|
||||
})
|
||||
.then(({ data, status }) => {
|
||||
if (status === 200) {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
let text = '';
|
||||
if (data.success === Object(data.success)) {
|
||||
text = data.success.message;
|
||||
} else if (data.OK === String(data.OK)) {
|
||||
text = data.OK;
|
||||
}
|
||||
if (text) {
|
||||
new Noty({
|
||||
type: 'success',
|
||||
text: escapeTag(text)
|
||||
}).show();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
if (status === 401) {
|
||||
if (data.error?.message === '"Missing Credentials"') {
|
||||
authStore.handleAutoLogin();
|
||||
$throw(
|
||||
401,
|
||||
t('api.error.message.missing_credentials'),
|
||||
endpoint
|
||||
);
|
||||
} else if (
|
||||
data.error.message === '"Unauthorized"' &&
|
||||
endpoint !== 'auth/user'
|
||||
) {
|
||||
// trigger 2FA dialog }
|
||||
if (!authStore.twoFactorAuthDialogVisible) {
|
||||
userStore.getCurrentUser();
|
||||
}
|
||||
$throw(401, t('api.status_code.401'), endpoint);
|
||||
}
|
||||
}
|
||||
if (status === 403 && endpoint === 'config') {
|
||||
$app.$alert(
|
||||
t('api.error.message.vpn_in_use'),
|
||||
`403 ${t('api.error.message.login_error')}`
|
||||
);
|
||||
authStore.handleLogoutEvent();
|
||||
$throw(403, endpoint);
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('avatars/')
|
||||
) {
|
||||
$app.$message({
|
||||
message: t('message.api_handler.avatar_private_or_deleted'),
|
||||
type: 'error'
|
||||
});
|
||||
avatarStore.avatarDialog.visible = false;
|
||||
$throw(404, data.error?.message || '', endpoint);
|
||||
}
|
||||
if (status === 404 && endpoint.endsWith('/persist/exists')) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
(status === 404 || status === 403) &&
|
||||
!endpoint.startsWith('auth/user')
|
||||
) {
|
||||
failedGetRequests.set(endpoint, Date.now());
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('users/') &&
|
||||
endpoint.split('/').length - 1 === 1
|
||||
) {
|
||||
$throw(404, data.error?.message || '', endpoint);
|
||||
}
|
||||
if (
|
||||
status === 404 &&
|
||||
endpoint.startsWith('invite/') &&
|
||||
init.inviteId
|
||||
) {
|
||||
notificationStore.expireNotification(init.inviteId);
|
||||
}
|
||||
if (status === 403 && endpoint.startsWith('invite/myself/to/')) {
|
||||
$throw(403, data.error?.message || '', endpoint);
|
||||
}
|
||||
if (data && data.error === Object(data.error)) {
|
||||
$throw(
|
||||
data.error.status_code || status,
|
||||
data.error.message,
|
||||
endpoint
|
||||
);
|
||||
} else if (data && typeof data.error === 'string') {
|
||||
$throw(data.status_code || status, data.error, endpoint);
|
||||
}
|
||||
$throw(status, data, endpoint);
|
||||
});
|
||||
if (init.method === 'GET') {
|
||||
req.finally(() => {
|
||||
pendingGetRequests.delete(init.url);
|
||||
});
|
||||
pendingGetRequests.set(init.url, {
|
||||
req,
|
||||
time: Date.now()
|
||||
});
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} code
|
||||
* @param {string|object} [error]
|
||||
* @param {string} [endpoint]
|
||||
*/
|
||||
export function $throw(code, error, endpoint) {
|
||||
let message = [];
|
||||
if (code > 0) {
|
||||
const status = statusCodes[code];
|
||||
if (typeof status === 'undefined') {
|
||||
message.push(`${code}`);
|
||||
} else {
|
||||
const codeText = t(`api.status_code.${code}`);
|
||||
message.push(`${code} ${codeText}`);
|
||||
}
|
||||
}
|
||||
if (typeof error !== 'undefined') {
|
||||
message.push(
|
||||
`${t('api.error.message.error_message')}: ${typeof error === 'string' ? error : JSON.stringify(error)}`
|
||||
);
|
||||
}
|
||||
if (typeof endpoint !== 'undefined') {
|
||||
message.push(
|
||||
`${t('api.error.message.endpoint')}: "${typeof endpoint === 'string' ? endpoint : JSON.stringify(endpoint)}"`
|
||||
);
|
||||
}
|
||||
const text = message.map((s) => escapeTag(s)).join('<br>');
|
||||
if (text.length) {
|
||||
if (AppGlobal.errorNoty) {
|
||||
AppGlobal.errorNoty.close();
|
||||
}
|
||||
AppGlobal.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text
|
||||
});
|
||||
AppGlobal.errorNoty.show();
|
||||
}
|
||||
const e = new Error(text);
|
||||
e.status = code;
|
||||
e.endpoint = endpoint;
|
||||
throw e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes data in bulk by making paginated requests until all data is fetched or limits are reached.
|
||||
*
|
||||
* @async
|
||||
* @function processBulk
|
||||
* @param {object} options - Configuration options for bulk processing
|
||||
* @param {function} options.fn - The function to call for each batch request. Must return a result with a 'json' property containing an array
|
||||
* @param {object} [options.params={}] - Parameters to pass to the function. Will be modified to include pagination
|
||||
* @param {number} [options.N=-1] - Maximum number of items to fetch. -1 for unlimited, 0 for fetch until page size not met
|
||||
* @param {string} [options.limitParam='n'] - The parameter name used for page size in the request
|
||||
* @param {function} [options.handle] - Callback function to handle each batch result
|
||||
* @param {function} [options.done] - Callback function called when processing is complete. Receives boolean indicating success
|
||||
* @returns {Promise<void>} Promise that resolves when bulk processing is complete
|
||||
*
|
||||
* @example
|
||||
* await processBulk({
|
||||
* fn: fetchUsers,
|
||||
* params: { n: 50 },
|
||||
* N: 200,
|
||||
* handle: (result) => console.log(`Fetched ${result.json.length} users`),
|
||||
* done: (success) => console.log(success ? 'Complete' : 'Failed')
|
||||
* });
|
||||
*/
|
||||
export async function processBulk(options) {
|
||||
const {
|
||||
fn,
|
||||
params: rawParams = {},
|
||||
N = -1,
|
||||
limitParam = 'n',
|
||||
handle,
|
||||
done
|
||||
} = options;
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = { ...rawParams };
|
||||
if (typeof params.offset !== 'number') {
|
||||
params.offset = 0;
|
||||
}
|
||||
const pageSize = params[limitParam];
|
||||
|
||||
let totalFetched = 0;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const result = await fn(params);
|
||||
const batchSize = result.json.length;
|
||||
|
||||
if (typeof handle === 'function') {
|
||||
handle(result);
|
||||
}
|
||||
if (batchSize === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (N > 0) {
|
||||
totalFetched += batchSize;
|
||||
if (totalFetched >= N) {
|
||||
break;
|
||||
}
|
||||
} else if (N === 0) {
|
||||
if (batchSize < pageSize) {
|
||||
break;
|
||||
}
|
||||
totalFetched += batchSize;
|
||||
} else {
|
||||
totalFetched += batchSize;
|
||||
}
|
||||
params.offset += batchSize;
|
||||
}
|
||||
|
||||
if (typeof done === 'function') {
|
||||
done(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Bulk processing error:', err);
|
||||
if (typeof done === 'function') {
|
||||
done(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// requires binding of SharedVariable
|
||||
|
||||
function transformKey(key) {
|
||||
return String(key).toLowerCase();
|
||||
}
|
||||
|
||||
class SharedRepository {
|
||||
remove(key) {
|
||||
var _key = transformKey(key);
|
||||
return SharedVariable.Remove(_key);
|
||||
}
|
||||
|
||||
async getString(key, defaultValue = null) {
|
||||
var _key = transformKey(key);
|
||||
var value = await SharedVariable.Get(_key);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setString(key, value) {
|
||||
var _key = transformKey(key);
|
||||
var _value = String(value);
|
||||
await SharedVariable.Set(_key, _value);
|
||||
}
|
||||
|
||||
async getBool(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
async setBool(key, value) {
|
||||
await this.setString(key, value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
async getInt(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setInt(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getFloat(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = parseFloat(value);
|
||||
if (isNaN(value) === true) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setFloat(key, value) {
|
||||
await this.setString(key, value);
|
||||
}
|
||||
|
||||
async getObject(key, defaultValue = null) {
|
||||
var value = await this.getString(key, null);
|
||||
if (value === null || value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (err) {}
|
||||
if (value !== Object(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setObject(key, value) {
|
||||
await this.setString(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
async getArray(key, defaultValue = null) {
|
||||
var value = await this.getObject(key, null);
|
||||
if (Array.isArray(value) === false) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async setArray(key, value) {
|
||||
await this.setObject(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
var self = new SharedRepository();
|
||||
window.sharedRepository = self;
|
||||
|
||||
export { self as default, SharedRepository };
|
||||
@@ -0,0 +1,7 @@
|
||||
import { reactive } from 'vue';
|
||||
const watchState = reactive({
|
||||
isLoggedIn: false,
|
||||
isFriendsLoaded: false
|
||||
});
|
||||
|
||||
export { watchState };
|
||||
@@ -13,6 +13,10 @@ class WebApiService {
|
||||
return WebApi.SetCookies(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} options
|
||||
* @returns {Promise<{status: number, data?: string}>}
|
||||
*/
|
||||
async execute(options) {
|
||||
if (!options) {
|
||||
throw new Error('options is required');
|
||||
|
||||
@@ -0,0 +1,572 @@
|
||||
import Noty from 'noty';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { groupRequest } from '../api';
|
||||
import { escapeTag, parseLocation } from '../shared/utils';
|
||||
import {
|
||||
useFriendStore,
|
||||
useGalleryStore,
|
||||
useGroupStore,
|
||||
useInstanceStore,
|
||||
useLocationStore,
|
||||
useNotificationStore,
|
||||
useSharedFeedStore,
|
||||
useUiStore,
|
||||
useUserStore
|
||||
} from '../stores';
|
||||
import { AppGlobal } from './appConfig';
|
||||
import { request } from './request';
|
||||
import { watchState } from './watchState';
|
||||
|
||||
let webSocket = null;
|
||||
let lastWebSocketMessage = '';
|
||||
|
||||
export function initWebsocket() {
|
||||
if (!watchState.isFriendsLoaded || webSocket !== null) {
|
||||
return;
|
||||
}
|
||||
return request('auth', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
if (args.json.ok) {
|
||||
connectWebSocket(args.json.token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} token
|
||||
* @returns {void}
|
||||
*/
|
||||
function connectWebSocket(token) {
|
||||
const userStore = useUserStore();
|
||||
if (webSocket !== null) {
|
||||
return;
|
||||
}
|
||||
const socket = new WebSocket(`${AppGlobal.websocketDomain}/?auth=${token}`);
|
||||
socket.onopen = () => {
|
||||
if (AppGlobal.debugWebSocket) {
|
||||
console.log('WebSocket connected');
|
||||
}
|
||||
};
|
||||
socket.onclose = () => {
|
||||
if (webSocket === socket) {
|
||||
webSocket = null;
|
||||
}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {
|
||||
console.error('Error closing WebSocket:', err);
|
||||
}
|
||||
if (AppGlobal.debugWebSocket) {
|
||||
console.log('WebSocket closed');
|
||||
}
|
||||
workerTimers.setTimeout(() => {
|
||||
if (
|
||||
watchState.isLoggedIn &&
|
||||
watchState.isFriendsLoaded &&
|
||||
webSocket === null
|
||||
) {
|
||||
initWebsocket();
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
socket.onerror = () => {
|
||||
if (AppGlobal.errorNoty) {
|
||||
AppGlobal.errorNoty.close();
|
||||
}
|
||||
AppGlobal.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: 'WebSocket Error'
|
||||
}).show();
|
||||
socket.onclose();
|
||||
};
|
||||
socket.onmessage = ({ data }) => {
|
||||
try {
|
||||
if (lastWebSocketMessage === data) {
|
||||
// pls no spam
|
||||
return;
|
||||
}
|
||||
lastWebSocketMessage = data;
|
||||
const json = JSON.parse(data);
|
||||
try {
|
||||
json.content = JSON.parse(json.content);
|
||||
} catch {
|
||||
// ignore parse error
|
||||
}
|
||||
handlePipeline({
|
||||
json
|
||||
});
|
||||
if (AppGlobal.debugWebSocket && json.content) {
|
||||
let displayName = '';
|
||||
const user = userStore.cachedUsers.get(json.content.userId);
|
||||
if (user) {
|
||||
displayName = user.displayName;
|
||||
}
|
||||
console.log('WebSocket', json.type, displayName, json.content);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
webSocket = socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function closeWebSocket() {
|
||||
const socket = webSocket;
|
||||
if (socket === null) {
|
||||
return;
|
||||
}
|
||||
webSocket = null;
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {
|
||||
console.error('Error closing WebSocket:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function reconnectWebSocket() {
|
||||
if (!watchState.isLoggedIn || !watchState.isFriendsLoaded) {
|
||||
return;
|
||||
}
|
||||
closeWebSocket();
|
||||
initWebsocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} args
|
||||
* @param {string} args.json.type
|
||||
*/
|
||||
function handlePipeline(args) {
|
||||
const userStore = useUserStore();
|
||||
const locationStore = useLocationStore();
|
||||
const galleryStore = useGalleryStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
const sharedFeedStore = useSharedFeedStore();
|
||||
const friendStore = useFriendStore();
|
||||
const groupStore = useGroupStore();
|
||||
const uiStore = useUiStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const { type, content, err } = args.json;
|
||||
if (typeof err !== 'undefined') {
|
||||
console.error('PIPELINE: error', args);
|
||||
if (AppGlobal.errorNoty) {
|
||||
AppGlobal.errorNoty.close();
|
||||
}
|
||||
AppGlobal.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: escapeTag(`WebSocket Error: ${err}`)
|
||||
}).show();
|
||||
return;
|
||||
}
|
||||
if (typeof content === 'undefined') {
|
||||
console.error('PIPELINE: missing content', args);
|
||||
return;
|
||||
}
|
||||
if (typeof content.user !== 'undefined') {
|
||||
// I forgot about this...
|
||||
delete content.user.state;
|
||||
}
|
||||
switch (type) {
|
||||
case 'notification':
|
||||
notificationStore.handleNotification({
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
notificationStore.handlePipelineNotification({
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2':
|
||||
console.log('notification-v2', content);
|
||||
notificationStore.handleNotificationV2({
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2-delete':
|
||||
console.log('notification-v2-delete', content);
|
||||
for (var id of content.ids) {
|
||||
notificationStore.handleNotificationHide({
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
notificationStore.handleNotificationSee({
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notification-v2-update':
|
||||
console.log('notification-v2-update', content);
|
||||
notificationStore.handleNotificationV2Update({
|
||||
json: content.updates,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'see-notification':
|
||||
notificationStore.handleNotificationSee({
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'hide-notification':
|
||||
notificationStore.handleNotificationHide({
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
notificationStore.handleNotificationSee({
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'response-notification':
|
||||
notificationStore.handleNotificationHide({
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
notificationStore.handleNotificationSee({
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-add':
|
||||
userStore.applyUser(content.user);
|
||||
friendStore.handleFriendAdd({
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-delete':
|
||||
friendStore.handleFriendDelete({
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-online':
|
||||
// Where is instanceId, travelingToWorld, travelingToInstance?
|
||||
// More JANK, what a mess
|
||||
const $location = parseLocation(content.location);
|
||||
const $travelingToLocation = parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (content?.user?.id) {
|
||||
const onlineJson = {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'online',
|
||||
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation: content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation.worldId,
|
||||
travelingToInstance: $travelingToLocation.instanceId,
|
||||
|
||||
...content.user
|
||||
};
|
||||
userStore.applyUser(onlineJson);
|
||||
} else {
|
||||
console.error('friend-online missing user id', content);
|
||||
friendStore.updateFriend(content.userId, 'online');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-active':
|
||||
if (content?.user?.id) {
|
||||
const activeJson = {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'active',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline',
|
||||
|
||||
...content.user
|
||||
};
|
||||
userStore.applyUser(activeJson);
|
||||
} else {
|
||||
console.error('friend-active missing user id', content);
|
||||
friendStore.updateFriend(content.userId, 'active');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-offline':
|
||||
// more JANK, hell yeah
|
||||
const offlineJson = {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'offline',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline'
|
||||
};
|
||||
userStore.applyUser(offlineJson);
|
||||
break;
|
||||
|
||||
case 'friend-update':
|
||||
userStore.applyUser(content.user);
|
||||
break;
|
||||
|
||||
case 'friend-location':
|
||||
const $location1 = parseLocation(content.location);
|
||||
const $travelingToLocation1 = parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (!content?.user?.id) {
|
||||
console.error('friend-location missing user id', content);
|
||||
const jankLocationJson = {
|
||||
id: content.userId,
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location1.instanceId,
|
||||
travelingToLocation: content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation1.worldId,
|
||||
travelingToInstance: $travelingToLocation1.instanceId
|
||||
};
|
||||
userStore.applyUser(jankLocationJson);
|
||||
break;
|
||||
}
|
||||
const locationJson = {
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location1.instanceId,
|
||||
travelingToLocation: content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation1.worldId,
|
||||
travelingToInstance: $travelingToLocation1.instanceId,
|
||||
...content.user,
|
||||
state: 'online' // JANK
|
||||
};
|
||||
userStore.applyUser(locationJson);
|
||||
|
||||
break;
|
||||
|
||||
case 'user-update':
|
||||
userStore.applyCurrentUser(content.user);
|
||||
break;
|
||||
|
||||
case 'user-location':
|
||||
// update current user location
|
||||
if (content.userId !== userStore.currentUser.id) {
|
||||
console.error('user-location wrong userId', content);
|
||||
break;
|
||||
}
|
||||
|
||||
// content.user: {} // we don't trust this
|
||||
// content.world: {} // this is long gone
|
||||
// content.worldId // where did worldId go?
|
||||
// content.instance // without worldId, this is useless
|
||||
|
||||
locationStore.setCurrentUserLocation(
|
||||
content.location,
|
||||
content.travelingToLocation
|
||||
);
|
||||
break;
|
||||
|
||||
case 'group-joined':
|
||||
// var groupId = content.groupId;
|
||||
// $app.onGroupJoined(groupId);
|
||||
break;
|
||||
|
||||
case 'group-left':
|
||||
// var groupId = content.groupId;
|
||||
// $app.onGroupLeft(groupId);
|
||||
break;
|
||||
|
||||
case 'group-role-updated':
|
||||
const groupId = content.role.groupId;
|
||||
groupRequest
|
||||
.getGroup({ groupId, includeRoles: true })
|
||||
.then((args) => groupStore.applyGroup(args.json));
|
||||
console.log('group-role-updated', content);
|
||||
|
||||
// content {
|
||||
// role: {
|
||||
// createdAt: string,
|
||||
// description: string,
|
||||
// groupId: string,
|
||||
// id: string,
|
||||
// isManagementRole: boolean,
|
||||
// isSelfAssignable: boolean,
|
||||
// name: string,
|
||||
// order: number,
|
||||
// permissions: string[],
|
||||
// requiresPurchase: boolean,
|
||||
// requiresTwoFactor: boolean
|
||||
break;
|
||||
|
||||
case 'group-member-updated':
|
||||
var member = content.member;
|
||||
if (!member) {
|
||||
console.error('group-member-updated missing member', content);
|
||||
break;
|
||||
}
|
||||
const groupId1 = member.groupId;
|
||||
if (
|
||||
groupStore.groupDialog.visible &&
|
||||
groupStore.groupDialog.id === groupId1
|
||||
) {
|
||||
groupStore.getGroupDialogGroup(groupId1);
|
||||
}
|
||||
groupStore.handleGroupMember({
|
||||
json: member,
|
||||
params: {
|
||||
groupId: groupId1
|
||||
}
|
||||
});
|
||||
console.log('group-member-updated', member);
|
||||
break;
|
||||
|
||||
case 'instance-queue-joined':
|
||||
case 'instance-queue-position':
|
||||
var instanceId = content.instanceLocation;
|
||||
var position = content.position ?? 0;
|
||||
var queueSize = content.queueSize ?? 0;
|
||||
instanceStore.instanceQueueUpdate(instanceId, position, queueSize);
|
||||
break;
|
||||
|
||||
case 'instance-queue-ready':
|
||||
// var expiry = Date.parse(content.expiry);
|
||||
instanceStore.instanceQueueReady(content.instanceLocation);
|
||||
break;
|
||||
|
||||
case 'instance-queue-left':
|
||||
instanceStore.removeQueuedInstance(content.instanceLocation);
|
||||
// $app.instanceQueueClear();
|
||||
break;
|
||||
|
||||
case 'content-refresh':
|
||||
var contentType = content.contentType;
|
||||
console.log('content-refresh', content);
|
||||
if (contentType === 'icon') {
|
||||
if (
|
||||
galleryStore.galleryDialogVisible &&
|
||||
!galleryStore.galleryDialogIconsLoading
|
||||
) {
|
||||
galleryStore.refreshVRCPlusIconsTable();
|
||||
}
|
||||
} else if (contentType === 'gallery') {
|
||||
if (
|
||||
galleryStore.galleryDialogVisible &&
|
||||
!galleryStore.galleryDialogGalleryLoading
|
||||
) {
|
||||
galleryStore.refreshGalleryTable();
|
||||
}
|
||||
} else if (contentType === 'emoji') {
|
||||
if (
|
||||
galleryStore.galleryDialogVisible &&
|
||||
!galleryStore.galleryDialogEmojisLoading
|
||||
) {
|
||||
galleryStore.refreshEmojiTable();
|
||||
}
|
||||
} else if (contentType === 'sticker') {
|
||||
// on sticker upload
|
||||
} else if (contentType === 'print') {
|
||||
if (content.actionType === 'created') {
|
||||
galleryStore.tryDeleteOldPrints();
|
||||
} else if (
|
||||
galleryStore.galleryDialogVisible &&
|
||||
!galleryStore.galleryDialogPrintsLoading
|
||||
) {
|
||||
galleryStore.refreshPrintTable();
|
||||
}
|
||||
} else if (contentType === 'prints') {
|
||||
// lol
|
||||
} else if (contentType === 'avatar') {
|
||||
// hmm, utilizing this might be too spamy and cause UI to move around
|
||||
} else if (contentType === 'world') {
|
||||
// hmm
|
||||
} else if (contentType === 'created') {
|
||||
// on avatar upload, might be gone now
|
||||
} else if (contentType === 'avatargallery') {
|
||||
// on avatar gallery image upload
|
||||
} else if (contentType === 'invitePhoto') {
|
||||
// on uploading invite photo
|
||||
} else if (contentType === 'inventory') {
|
||||
if (
|
||||
galleryStore.galleryDialogVisible &&
|
||||
!galleryStore.galleryDialogInventoryLoading
|
||||
) {
|
||||
galleryStore.getInventory();
|
||||
}
|
||||
// on consuming a bundle
|
||||
// {contentType: 'inventory', itemId: 'inv_', itemType: 'prop', actionType: 'add'}
|
||||
} else if (!contentType) {
|
||||
console.log('content-refresh without contentType', content);
|
||||
} else {
|
||||
console.log(
|
||||
'Unknown content-refresh type',
|
||||
content.contentType
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'instance-closed':
|
||||
// TODO: get worldName, groupName, hardClose
|
||||
const noty = {
|
||||
type: 'instance.closed',
|
||||
location: content.instanceLocation,
|
||||
message: 'Instance Closed',
|
||||
created_at: new Date().toJSON()
|
||||
};
|
||||
if (
|
||||
notificationStore.notificationTable.filters[0].value.length ===
|
||||
0 ||
|
||||
notificationStore.notificationTable.filters[0].value.includes(
|
||||
noty.type
|
||||
)
|
||||
) {
|
||||
uiStore.notifyMenu('notification');
|
||||
}
|
||||
notificationStore.queueNotificationNoty(noty);
|
||||
notificationStore.notificationTable.data.push(noty);
|
||||
sharedFeedStore.updateSharedFeed(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown pipeline type', args.json);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user