mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 05:43:51 +02:00
refactor: dialogs (#1224)
* refactor: dialogs * fix: storeAvatarImage * FriendLog.vue * FriendLog.vue * FriendLog.vue * GameLog.vue * fix: next day button jumping to the wrong date * sync master * fix: launchGame * Notification.vue * Feed.vue * Search.vue * Profile.vue * PlayerList.vue * Login.vue * utils * update dialog * del gameLog.pug * fix * fix: group role cannot be displayed currently * fix: "Hide Friends in Same Instance" hides players in unrelated private instances (#1210) * fix * fix: "Hide Friends in Same Instance" does not work when "Split Favorite Friends" is enabled * fix Notification.vue message * fix: deleteFavoriteNoConfirm * fix: feed status style * fix: infinite loading when deleting note * fix: private players will not be hidden when 'Hide Friends in Same Instance', and 'Hide Friends in Same Instance' will not work when 'Split Favorite Friends'
This commit is contained in:
125
src/composables/avatar/utils.js
Normal file
125
src/composables/avatar/utils.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import utils from '../../classes/utils';
|
||||
|
||||
function storeAvatarImage(args) {
|
||||
const refCreatedAt = args.json.versions[0];
|
||||
const fileCreatedAt = refCreatedAt.created_at;
|
||||
const fileId = args.params.fileId;
|
||||
let avatarName = '';
|
||||
const imageName = args.json.name;
|
||||
const avatarNameRegex = /Avatar - (.*) - Image -/gi.exec(imageName);
|
||||
if (avatarNameRegex) {
|
||||
avatarName = utils.replaceBioSymbols(avatarNameRegex[1]);
|
||||
}
|
||||
const ownerId = args.json.ownerId;
|
||||
const avatarInfo = {
|
||||
ownerId,
|
||||
avatarName,
|
||||
fileCreatedAt
|
||||
};
|
||||
window.API.cachedAvatarNames.set(fileId, avatarInfo);
|
||||
return avatarInfo;
|
||||
}
|
||||
|
||||
function parseAvatarUrl(avatar) {
|
||||
const url = new URL(avatar);
|
||||
const urlPath = url.pathname;
|
||||
if (urlPath.substring(5, 13) === '/avatar/') {
|
||||
const avatarId = urlPath.substring(13);
|
||||
return avatarId;
|
||||
}
|
||||
// return void 0;
|
||||
}
|
||||
|
||||
function getPlatformInfo(unityPackages) {
|
||||
var pc = {};
|
||||
var android = {};
|
||||
var ios = {};
|
||||
if (typeof unityPackages === 'object') {
|
||||
for (var unityPackage of unityPackages) {
|
||||
if (
|
||||
unityPackage.variant &&
|
||||
unityPackage.variant !== 'standard' &&
|
||||
unityPackage.variant !== 'security'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (unityPackage.platform === 'standalonewindows') {
|
||||
if (
|
||||
unityPackage.performanceRating === 'None' &&
|
||||
pc.performanceRating
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
pc = unityPackage;
|
||||
} else if (unityPackage.platform === 'android') {
|
||||
if (
|
||||
unityPackage.performanceRating === 'None' &&
|
||||
android.performanceRating
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
android = unityPackage;
|
||||
} else if (unityPackage.platform === 'ios') {
|
||||
if (
|
||||
unityPackage.performanceRating === 'None' &&
|
||||
ios.performanceRating
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
ios = unityPackage;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { pc, android, ios };
|
||||
}
|
||||
|
||||
function compareUnityVersion(unitySortNumber) {
|
||||
if (!window.API.cachedConfig.sdkUnityVersion) {
|
||||
console.error('No cachedConfig.sdkUnityVersion');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2022.3.6f1 2022 03 06 000
|
||||
// 2019.4.31f1 2019 04 31 000
|
||||
// 5.3.4p1 5 03 04 010
|
||||
// 2019.4.31f1c1 is a thing
|
||||
var array = window.API.cachedConfig.sdkUnityVersion.split('.');
|
||||
if (array.length < 3) {
|
||||
console.error('Invalid cachedConfig.sdkUnityVersion');
|
||||
return false;
|
||||
}
|
||||
var currentUnityVersion = array[0];
|
||||
currentUnityVersion += array[1].padStart(2, '0');
|
||||
var indexFirstLetter = array[2].search(/[a-zA-Z]/);
|
||||
if (indexFirstLetter > -1) {
|
||||
currentUnityVersion += array[2]
|
||||
.substr(0, indexFirstLetter)
|
||||
.padStart(2, '0');
|
||||
currentUnityVersion += '0';
|
||||
var letter = array[2].substr(indexFirstLetter, 1);
|
||||
if (letter === 'p') {
|
||||
currentUnityVersion += '1';
|
||||
} else {
|
||||
// f
|
||||
currentUnityVersion += '0';
|
||||
}
|
||||
currentUnityVersion += '0';
|
||||
} else {
|
||||
// just in case
|
||||
currentUnityVersion += '000';
|
||||
}
|
||||
// just in case
|
||||
currentUnityVersion = currentUnityVersion.replace(/\D/g, '');
|
||||
|
||||
if (parseInt(unitySortNumber, 10) <= parseInt(currentUnityVersion, 10)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export {
|
||||
storeAvatarImage,
|
||||
parseAvatarUrl,
|
||||
getPlatformInfo,
|
||||
compareUnityVersion
|
||||
};
|
||||
14
src/composables/group/utils.js
Normal file
14
src/composables/group/utils.js
Normal file
@@ -0,0 +1,14 @@
|
||||
function hasGroupPermission(ref, permission) {
|
||||
if (
|
||||
ref &&
|
||||
ref.myMember &&
|
||||
ref.myMember.permissions &&
|
||||
(ref.myMember.permissions.includes('*') ||
|
||||
ref.myMember.permissions.includes(permission))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export { hasGroupPermission };
|
||||
168
src/composables/instance/utils.js
Normal file
168
src/composables/instance/utils.js
Normal file
@@ -0,0 +1,168 @@
|
||||
import { instanceRequest } from '../../api';
|
||||
|
||||
// TODO: launch, invite, refresh, etc. buttons, better to split into one component
|
||||
function refreshInstancePlayerCount(instance) {
|
||||
const L = parseLocation(instance);
|
||||
if (L.isRealInstance) {
|
||||
instanceRequest.getInstance({
|
||||
worldId: L.worldId,
|
||||
instanceId: L.instanceId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isRealInstance(instanceId) {
|
||||
if (!instanceId) {
|
||||
return false;
|
||||
}
|
||||
switch (instanceId) {
|
||||
case ':':
|
||||
case 'offline':
|
||||
case 'offline:offline':
|
||||
case 'private':
|
||||
case 'private:private':
|
||||
case 'traveling':
|
||||
case 'traveling:traveling':
|
||||
case instanceId.startsWith('local'):
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function displayLocation(location, worldName, groupName) {
|
||||
var text = worldName;
|
||||
var L = parseLocation(location);
|
||||
if (L.isOffline) {
|
||||
text = 'Offline';
|
||||
} else if (L.isPrivate) {
|
||||
text = 'Private';
|
||||
} else if (L.isTraveling) {
|
||||
text = 'Traveling';
|
||||
} else if (L.worldId) {
|
||||
if (groupName) {
|
||||
text = `${worldName} ${L.accessTypeName}(${groupName})`;
|
||||
} else if (L.instanceId) {
|
||||
text = `${worldName} ${L.accessTypeName}`;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function parseLocation(tag) {
|
||||
var _tag = String(tag || '');
|
||||
var ctx = {
|
||||
tag: _tag,
|
||||
isOffline: false,
|
||||
isPrivate: false,
|
||||
isTraveling: false,
|
||||
isRealInstance: false,
|
||||
worldId: '',
|
||||
instanceId: '',
|
||||
instanceName: '',
|
||||
accessType: '',
|
||||
accessTypeName: '',
|
||||
region: '',
|
||||
shortName: '',
|
||||
userId: null,
|
||||
hiddenId: null,
|
||||
privateId: null,
|
||||
friendsId: null,
|
||||
groupId: null,
|
||||
groupAccessType: null,
|
||||
canRequestInvite: false,
|
||||
strict: false,
|
||||
ageGate: false
|
||||
};
|
||||
if (_tag === 'offline' || _tag === 'offline:offline') {
|
||||
ctx.isOffline = true;
|
||||
} else if (_tag === 'private' || _tag === 'private:private') {
|
||||
ctx.isPrivate = true;
|
||||
} else if (_tag === 'traveling' || _tag === 'traveling:traveling') {
|
||||
ctx.isTraveling = true;
|
||||
} else if (!_tag.startsWith('local')) {
|
||||
ctx.isRealInstance = true;
|
||||
var sep = _tag.indexOf(':');
|
||||
// technically not part of instance id, but might be there when coping id from url so why not support it
|
||||
var shortNameQualifier = '&shortName=';
|
||||
var shortNameIndex = _tag.indexOf(shortNameQualifier);
|
||||
if (shortNameIndex >= 0) {
|
||||
ctx.shortName = _tag.substr(
|
||||
shortNameIndex + shortNameQualifier.length
|
||||
);
|
||||
_tag = _tag.substr(0, shortNameIndex);
|
||||
}
|
||||
if (sep >= 0) {
|
||||
ctx.worldId = _tag.substr(0, sep);
|
||||
ctx.instanceId = _tag.substr(sep + 1);
|
||||
ctx.instanceId.split('~').forEach((s, i) => {
|
||||
if (i) {
|
||||
var A = s.indexOf('(');
|
||||
var Z = A >= 0 ? s.lastIndexOf(')') : -1;
|
||||
var key = Z >= 0 ? s.substr(0, A) : s;
|
||||
var value = A < Z ? s.substr(A + 1, Z - A - 1) : '';
|
||||
if (key === 'hidden') {
|
||||
ctx.hiddenId = value;
|
||||
} else if (key === 'private') {
|
||||
ctx.privateId = value;
|
||||
} else if (key === 'friends') {
|
||||
ctx.friendsId = value;
|
||||
} else if (key === 'canRequestInvite') {
|
||||
ctx.canRequestInvite = true;
|
||||
} else if (key === 'region') {
|
||||
ctx.region = value;
|
||||
} else if (key === 'group') {
|
||||
ctx.groupId = value;
|
||||
} else if (key === 'groupAccessType') {
|
||||
ctx.groupAccessType = value;
|
||||
} else if (key === 'strict') {
|
||||
ctx.strict = true;
|
||||
} else if (key === 'ageGate') {
|
||||
ctx.ageGate = true;
|
||||
}
|
||||
} else {
|
||||
ctx.instanceName = s;
|
||||
}
|
||||
});
|
||||
ctx.accessType = 'public';
|
||||
if (ctx.privateId !== null) {
|
||||
if (ctx.canRequestInvite) {
|
||||
// InvitePlus
|
||||
ctx.accessType = 'invite+';
|
||||
} else {
|
||||
// InviteOnly
|
||||
ctx.accessType = 'invite';
|
||||
}
|
||||
ctx.userId = ctx.privateId;
|
||||
} else if (ctx.friendsId !== null) {
|
||||
// FriendsOnly
|
||||
ctx.accessType = 'friends';
|
||||
ctx.userId = ctx.friendsId;
|
||||
} else if (ctx.hiddenId !== null) {
|
||||
// FriendsOfGuests
|
||||
ctx.accessType = 'friends+';
|
||||
ctx.userId = ctx.hiddenId;
|
||||
} else if (ctx.groupId !== null) {
|
||||
// Group
|
||||
ctx.accessType = 'group';
|
||||
}
|
||||
ctx.accessTypeName = ctx.accessType;
|
||||
if (ctx.groupAccessType !== null) {
|
||||
if (ctx.groupAccessType === 'public') {
|
||||
ctx.accessTypeName = 'groupPublic';
|
||||
} else if (ctx.groupAccessType === 'plus') {
|
||||
ctx.accessTypeName = 'groupPlus';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.worldId = _tag;
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export {
|
||||
refreshInstancePlayerCount,
|
||||
isRealInstance,
|
||||
displayLocation,
|
||||
parseLocation
|
||||
};
|
||||
@@ -1,19 +1,3 @@
|
||||
function getVRChatResolution(res) {
|
||||
switch (res) {
|
||||
case '1280x720':
|
||||
return '1280x720 (720p)';
|
||||
case '1920x1080':
|
||||
return '1920x1080 (1080p)';
|
||||
case '2560x1440':
|
||||
return '2560x1440 (2K)';
|
||||
case '3840x2160':
|
||||
return '3840x2160 (4K)';
|
||||
case '7680x4320':
|
||||
return '7680x4320 (8K)';
|
||||
}
|
||||
return `${res} (Custom)`;
|
||||
}
|
||||
|
||||
const VRChatScreenshotResolutions = [
|
||||
{ name: '1280x720 (720p)', width: 1280, height: 720 },
|
||||
{ name: '1920x1080 (1080p Default)', width: '', height: '' },
|
||||
@@ -29,8 +13,4 @@ const VRChatCameraResolutions = [
|
||||
{ name: '7680x4320 (8K)', width: 7680, height: 4320 }
|
||||
];
|
||||
|
||||
export {
|
||||
getVRChatResolution,
|
||||
VRChatScreenshotResolutions,
|
||||
VRChatCameraResolutions
|
||||
};
|
||||
export { VRChatScreenshotResolutions, VRChatCameraResolutions };
|
||||
17
src/composables/setting/utils.js
Normal file
17
src/composables/setting/utils.js
Normal file
@@ -0,0 +1,17 @@
|
||||
function getVRChatResolution(res) {
|
||||
switch (res) {
|
||||
case '1280x720':
|
||||
return '1280x720 (720p)';
|
||||
case '1920x1080':
|
||||
return '1920x1080 (1080p)';
|
||||
case '2560x1440':
|
||||
return '2560x1440 (2K)';
|
||||
case '3840x2160':
|
||||
return '3840x2160 (4K)';
|
||||
case '7680x4320':
|
||||
return '7680x4320 (8K)';
|
||||
}
|
||||
return `${res} (Custom)`;
|
||||
}
|
||||
|
||||
export { getVRChatResolution };
|
||||
106
src/composables/shared/constants/photon.js
Normal file
106
src/composables/shared/constants/photon.js
Normal file
@@ -0,0 +1,106 @@
|
||||
const photonEmojis = [
|
||||
'Angry',
|
||||
'Blushing',
|
||||
'Crying',
|
||||
'Frown',
|
||||
'Hand Wave',
|
||||
'Hang Ten',
|
||||
'In Love',
|
||||
'Jack O Lantern',
|
||||
'Kiss',
|
||||
'Laugh',
|
||||
'Skull',
|
||||
'Smile',
|
||||
'Spooky Ghost',
|
||||
'Stoic',
|
||||
'Sunglasses',
|
||||
'Thinking',
|
||||
'Thumbs Down',
|
||||
'Thumbs Up',
|
||||
'Tongue Out',
|
||||
'Wow',
|
||||
'Arrow Point',
|
||||
"Can't see",
|
||||
'Hourglass',
|
||||
'Keyboard',
|
||||
'No Headphones',
|
||||
'No Mic',
|
||||
'Portal',
|
||||
'Shush',
|
||||
'Bats',
|
||||
'Cloud',
|
||||
'Fire',
|
||||
'Snow Fall',
|
||||
'Snowball',
|
||||
'Splash',
|
||||
'Web',
|
||||
'Beer',
|
||||
'Candy',
|
||||
'Candy Cane',
|
||||
'Candy Corn',
|
||||
'Champagne',
|
||||
'Drink',
|
||||
'Gingerbread',
|
||||
'Ice Cream',
|
||||
'Pineapple',
|
||||
'Pizza',
|
||||
'Tomato',
|
||||
'Beachball',
|
||||
'Coal',
|
||||
'Confetti',
|
||||
'Gift',
|
||||
'Gifts',
|
||||
'Life Ring',
|
||||
'Mistletoe',
|
||||
'Money',
|
||||
'Neon Shades',
|
||||
'Sun Lotion',
|
||||
'Boo',
|
||||
'Broken Heart',
|
||||
'Exclamation',
|
||||
'Go',
|
||||
'Heart',
|
||||
'Music Note',
|
||||
'Question',
|
||||
'Stop',
|
||||
'Zzz'
|
||||
];
|
||||
|
||||
const photonEventType = [
|
||||
'MeshVisibility',
|
||||
'AnimationFloat',
|
||||
'AnimationBool',
|
||||
'AnimationTrigger',
|
||||
'AudioTrigger',
|
||||
'PlayAnimation',
|
||||
'SendMessage',
|
||||
'SetParticlePlaying',
|
||||
'TeleportPlayer',
|
||||
'RunConsoleCommand',
|
||||
'SetGameObjectActive',
|
||||
'SetWebPanelURI',
|
||||
'SetWebPanelVolume',
|
||||
'SpawnObject',
|
||||
'SendRPC',
|
||||
'ActivateCustomTrigger',
|
||||
'DestroyObject',
|
||||
'SetLayer',
|
||||
'SetMaterial',
|
||||
'AddHealth',
|
||||
'AddDamage',
|
||||
'SetComponentActive',
|
||||
'AnimationInt',
|
||||
'AnimationIntAdd',
|
||||
'AnimationIntSubtract',
|
||||
'AnimationIntMultiply',
|
||||
'AnimationIntDivide',
|
||||
'AddVelocity',
|
||||
'SetVelocity',
|
||||
'AddAngularVelocity',
|
||||
'SetAngularVelocity',
|
||||
'AddForce',
|
||||
'SetUIText',
|
||||
'CallUdonMethod'
|
||||
];
|
||||
|
||||
export { photonEmojis, photonEventType };
|
||||
318
src/composables/shared/utils.js
Normal file
318
src/composables/shared/utils.js
Normal file
@@ -0,0 +1,318 @@
|
||||
import Noty from 'noty';
|
||||
import utils from '../../classes/utils';
|
||||
import { compareUnityVersion } from '../avatar/utils';
|
||||
|
||||
function getAvailablePlatforms(unityPackages) {
|
||||
var isPC = false;
|
||||
var isQuest = false;
|
||||
var isIos = false;
|
||||
if (typeof unityPackages === 'object') {
|
||||
for (var unityPackage of unityPackages) {
|
||||
if (
|
||||
unityPackage.variant &&
|
||||
unityPackage.variant !== 'standard' &&
|
||||
unityPackage.variant !== 'security'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (unityPackage.platform === 'standalonewindows') {
|
||||
isPC = true;
|
||||
} else if (unityPackage.platform === 'android') {
|
||||
isQuest = true;
|
||||
} else if (unityPackage.platform === 'ios') {
|
||||
isIos = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { isPC, isQuest, isIos };
|
||||
}
|
||||
|
||||
function downloadAndSaveJson(fileName, data) {
|
||||
if (!fileName || !data) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute(
|
||||
'href',
|
||||
`data:application/json;charset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify(data, null, 2)
|
||||
)}`
|
||||
);
|
||||
link.setAttribute('download', `${fileName}.json`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} catch {
|
||||
new Noty({
|
||||
type: 'error',
|
||||
text: utils.escapeTag('Failed to download JSON.')
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteVRChatCache(ref) {
|
||||
var assetUrl = '';
|
||||
var variant = '';
|
||||
for (var i = ref.unityPackages.length - 1; i > -1; i--) {
|
||||
var unityPackage = ref.unityPackages[i];
|
||||
if (
|
||||
unityPackage.variant &&
|
||||
unityPackage.variant !== 'standard' &&
|
||||
unityPackage.variant !== 'security'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
unityPackage.platform === 'standalonewindows' &&
|
||||
compareUnityVersion(unityPackage.unitySortNumber)
|
||||
) {
|
||||
assetUrl = unityPackage.assetUrl;
|
||||
if (unityPackage.variant !== 'standard') {
|
||||
variant = unityPackage.variant;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
var id = extractFileId(assetUrl);
|
||||
var version = parseInt(extractFileVersion(assetUrl), 10);
|
||||
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
|
||||
await AssetBundleManager.DeleteCache(id, version, variant, variantVersion);
|
||||
}
|
||||
|
||||
async function checkVRChatCache(ref) {
|
||||
if (!ref.unityPackages) {
|
||||
return { Item1: -1, Item2: false, Item3: '' };
|
||||
}
|
||||
var assetUrl = '';
|
||||
var variant = '';
|
||||
for (var i = ref.unityPackages.length - 1; i > -1; i--) {
|
||||
var unityPackage = ref.unityPackages[i];
|
||||
if (unityPackage.variant && unityPackage.variant !== 'security') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
unityPackage.platform === 'standalonewindows' &&
|
||||
compareUnityVersion(unityPackage.unitySortNumber)
|
||||
) {
|
||||
assetUrl = unityPackage.assetUrl;
|
||||
if (unityPackage.variant !== 'standard') {
|
||||
variant = unityPackage.variant;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!assetUrl) {
|
||||
assetUrl = ref.assetUrl;
|
||||
}
|
||||
var id = extractFileId(assetUrl);
|
||||
var version = parseInt(extractFileVersion(assetUrl), 10);
|
||||
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
|
||||
if (!id || !version) {
|
||||
return { Item1: -1, Item2: false, Item3: '' };
|
||||
}
|
||||
|
||||
return AssetBundleManager.CheckVRChatCache(
|
||||
id,
|
||||
version,
|
||||
variant,
|
||||
variantVersion
|
||||
);
|
||||
}
|
||||
|
||||
function copyToClipboard(text, message = 'Copied successfully!') {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
window.$app.$message({
|
||||
message: message,
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Copy failed:', err);
|
||||
window.$app.$message.error('Copy failed!');
|
||||
});
|
||||
}
|
||||
|
||||
function getFaviconUrl(resource) {
|
||||
try {
|
||||
const url = new URL(resource);
|
||||
return `https://icons.duckduckgo.com/ip2/${url.host}.ico`;
|
||||
} catch (err) {
|
||||
console.error('Invalid URL:', err);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function convertFileUrlToImageUrl(url, resolution = 128) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* possible patterns?
|
||||
* /file/file_fileId/version
|
||||
* /file/file_fileId/version/
|
||||
* /file/file_fileId/version/file
|
||||
* /file/file_fileId/version/file/
|
||||
*/
|
||||
const pattern = /file\/file_([a-f0-9-]+)\/(\d+)(\/file)?\/?$/;
|
||||
const match = url.match(pattern);
|
||||
|
||||
if (match) {
|
||||
const fileId = match[1];
|
||||
const version = match[2];
|
||||
return `https://api.vrchat.cloud/api/1/image/file_${fileId}/${version}/${resolution}`;
|
||||
}
|
||||
// no match return origin url
|
||||
return url;
|
||||
}
|
||||
|
||||
function replaceVrcPackageUrl(url) {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
return url.replace('https://api.vrchat.cloud/', 'https://vrchat.com/');
|
||||
}
|
||||
|
||||
function getLaunchURL(instance) {
|
||||
var L = instance;
|
||||
if (L.instanceId) {
|
||||
if (L.shortName) {
|
||||
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
|
||||
L.worldId
|
||||
)}&instanceId=${encodeURIComponent(
|
||||
L.instanceId
|
||||
)}&shortName=${encodeURIComponent(L.shortName)}`;
|
||||
}
|
||||
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
|
||||
L.worldId
|
||||
)}&instanceId=${encodeURIComponent(L.instanceId)}`;
|
||||
}
|
||||
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
|
||||
L.worldId
|
||||
)}`;
|
||||
}
|
||||
|
||||
function extractFileId(s) {
|
||||
var match = String(s).match(/file_[0-9A-Za-z-]+/);
|
||||
return match ? match[0] : '';
|
||||
}
|
||||
|
||||
function extractFileVersion(s) {
|
||||
var match = /(?:\/file_[0-9A-Za-z-]+\/)([0-9]+)/gi.exec(s);
|
||||
return match ? match[1] : '';
|
||||
}
|
||||
|
||||
function extractVariantVersion(url) {
|
||||
if (!url) {
|
||||
return '0';
|
||||
}
|
||||
try {
|
||||
const params = new URLSearchParams(new URL(url).search);
|
||||
const version = params.get('v');
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
return '0';
|
||||
} catch {
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getAvailablePlatforms,
|
||||
downloadAndSaveJson,
|
||||
deleteVRChatCache,
|
||||
checkVRChatCache,
|
||||
copyToClipboard,
|
||||
getFaviconUrl,
|
||||
convertFileUrlToImageUrl,
|
||||
replaceVrcPackageUrl,
|
||||
getLaunchURL,
|
||||
extractFileId,
|
||||
extractFileVersion,
|
||||
extractVariantVersion
|
||||
};
|
||||
|
||||
// ---------------------- devtools method --------------------------
|
||||
|
||||
// not window.$app
|
||||
window.getBundleLocation = async function (input) {
|
||||
const $app = window.$app;
|
||||
var assetUrl = input;
|
||||
var variant = '';
|
||||
if (assetUrl) {
|
||||
// continue
|
||||
} else if (
|
||||
$app.avatarDialog.visible &&
|
||||
$app.avatarDialog.ref.unityPackages.length > 0
|
||||
) {
|
||||
var unityPackages = $app.avatarDialog.ref.unityPackages;
|
||||
for (let i = unityPackages.length - 1; i > -1; i--) {
|
||||
var unityPackage = unityPackages[i];
|
||||
if (
|
||||
unityPackage.variant &&
|
||||
unityPackage.variant !== 'standard' &&
|
||||
unityPackage.variant !== 'security'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
unityPackage.platform === 'standalonewindows' &&
|
||||
compareUnityVersion(unityPackage.unitySortNumber)
|
||||
) {
|
||||
assetUrl = unityPackage.assetUrl;
|
||||
if (unityPackage.variant !== 'standard') {
|
||||
variant = unityPackage.variant;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ($app.avatarDialog.visible && $app.avatarDialog.ref.assetUrl) {
|
||||
assetUrl = $app.avatarDialog.ref.assetUrl;
|
||||
} else if (
|
||||
$app.worldDialog.visible &&
|
||||
$app.worldDialog.ref.unityPackages.length > 0
|
||||
) {
|
||||
var unityPackages = $app.worldDialog.ref.unityPackages;
|
||||
for (let i = unityPackages.length - 1; i > -1; i--) {
|
||||
var unityPackage = unityPackages[i];
|
||||
if (
|
||||
unityPackage.platform === 'standalonewindows' &&
|
||||
compareUnityVersion(unityPackage.unitySortNumber)
|
||||
) {
|
||||
assetUrl = unityPackage.assetUrl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ($app.worldDialog.visible && $app.worldDialog.ref.assetUrl) {
|
||||
assetUrl = $app.worldDialog.ref.assetUrl;
|
||||
}
|
||||
if (!assetUrl) {
|
||||
return null;
|
||||
}
|
||||
var fileId = extractFileId(assetUrl);
|
||||
var fileVersion = parseInt(extractFileVersion(assetUrl), 10);
|
||||
var variantVersion = parseInt(extractVariantVersion(assetUrl), 10);
|
||||
var assetLocation = await AssetBundleManager.GetVRChatCacheFullLocation(
|
||||
fileId,
|
||||
fileVersion,
|
||||
variant,
|
||||
variantVersion
|
||||
);
|
||||
var cacheInfo = await AssetBundleManager.CheckVRChatCache(
|
||||
fileId,
|
||||
fileVersion,
|
||||
variant,
|
||||
variantVersion
|
||||
);
|
||||
var inCache = false;
|
||||
if (cacheInfo.Item1 > 0) {
|
||||
inCache = true;
|
||||
}
|
||||
console.log(`InCache: ${inCache}`);
|
||||
var fullAssetLocation = `${assetLocation}\\__data`;
|
||||
console.log(fullAssetLocation);
|
||||
return fullAssetLocation;
|
||||
};
|
||||
34
src/composables/user/constants/emoji.js
Normal file
34
src/composables/user/constants/emoji.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const emojiAnimationStyleUrl =
|
||||
'https://assets.vrchat.com/www/images/emoji-previews/';
|
||||
|
||||
const emojiAnimationStyleList = {
|
||||
Aura: 'Preview_B2-Aura.gif',
|
||||
Bats: 'Preview_B2-Fall_Bats.gif',
|
||||
Bees: 'Preview_B2-Bees.gif',
|
||||
Bounce: 'Preview_B2-Bounce.gif',
|
||||
Cloud: 'Preview_B2-Cloud.gif',
|
||||
Confetti: 'Preview_B2-Winter_Confetti.gif',
|
||||
Crying: 'Preview_B2-Crying.gif',
|
||||
Dislike: 'Preview_B2-Dislike.gif',
|
||||
Fire: 'Preview_B2-Fire.gif',
|
||||
Idea: 'Preview_B2-Idea.gif',
|
||||
Lasers: 'Preview_B2-Lasers.gif',
|
||||
Like: 'Preview_B2-Like.gif',
|
||||
Magnet: 'Preview_B2-Magnet.gif',
|
||||
Mistletoe: 'Preview_B2-Winter_Mistletoe.gif',
|
||||
Money: 'Preview_B2-Money.gif',
|
||||
Noise: 'Preview_B2-Noise.gif',
|
||||
Orbit: 'Preview_B2-Orbit.gif',
|
||||
Pizza: 'Preview_B2-Pizza.gif',
|
||||
Rain: 'Preview_B2-Rain.gif',
|
||||
Rotate: 'Preview_B2-Rotate.gif',
|
||||
Shake: 'Preview_B2-Shake.gif',
|
||||
Snow: 'Preview_B2-Spin.gif',
|
||||
Snowball: 'Preview_B2-Winter_Snowball.gif',
|
||||
Spin: 'Preview_B2-Spin.gif',
|
||||
Splash: 'Preview_B2-SummerSplash.gif',
|
||||
Stop: 'Preview_B2-Stop.gif',
|
||||
ZZZ: 'Preview_B2-ZZZ.gif'
|
||||
};
|
||||
|
||||
export { emojiAnimationStyleUrl, emojiAnimationStyleList };
|
||||
73
src/composables/user/constants/language.js
Normal file
73
src/composables/user/constants/language.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// vrchat to famfamfam language mappings
|
||||
const languageMappings = {
|
||||
eng: 'us',
|
||||
kor: 'kr',
|
||||
rus: 'ru',
|
||||
spa: 'es',
|
||||
por: 'pt',
|
||||
zho: 'cn',
|
||||
deu: 'de',
|
||||
jpn: 'jp',
|
||||
fra: 'fr',
|
||||
swe: 'se',
|
||||
nld: 'nl',
|
||||
pol: 'pl',
|
||||
dan: 'dk',
|
||||
nor: 'no',
|
||||
ita: 'it',
|
||||
tha: 'th',
|
||||
fin: 'fi',
|
||||
hun: 'hu',
|
||||
ces: 'cz',
|
||||
tur: 'tr',
|
||||
ara: 'ae',
|
||||
ron: 'ro',
|
||||
vie: 'vn',
|
||||
ukr: 'ua',
|
||||
ase: 'us',
|
||||
bfi: 'gb',
|
||||
dse: 'nl',
|
||||
fsl: 'fr',
|
||||
jsl: 'jp',
|
||||
kvk: 'kr',
|
||||
|
||||
mlt: 'mt',
|
||||
ind: 'id',
|
||||
hrv: 'hr',
|
||||
heb: 'he',
|
||||
afr: 'af',
|
||||
ben: 'be',
|
||||
bul: 'bg',
|
||||
cmn: 'cn',
|
||||
cym: 'cy',
|
||||
ell: 'el',
|
||||
est: 'et',
|
||||
fil: 'ph',
|
||||
gla: 'gd',
|
||||
gle: 'ga',
|
||||
hin: 'hi',
|
||||
hmn: 'cn',
|
||||
hye: 'hy',
|
||||
isl: 'is',
|
||||
lav: 'lv',
|
||||
lit: 'lt',
|
||||
ltz: 'lb',
|
||||
mar: 'hi',
|
||||
mkd: 'mk',
|
||||
msa: 'my',
|
||||
sco: 'gd',
|
||||
slk: 'sk',
|
||||
slv: 'sl',
|
||||
tel: 'hi',
|
||||
mri: 'nz',
|
||||
wuu: 'cn',
|
||||
yue: 'cn',
|
||||
tws: 'cn',
|
||||
asf: 'au',
|
||||
nzs: 'nz',
|
||||
gsg: 'de',
|
||||
epo: 'eo',
|
||||
tok: 'tok'
|
||||
};
|
||||
|
||||
export { languageMappings };
|
||||
@@ -0,0 +1,16 @@
|
||||
const userDialogGroupSortingOptions = {
|
||||
alphabetical: {
|
||||
name: 'dialog.user.groups.sorting.alphabetical',
|
||||
value: 'alphabetical'
|
||||
},
|
||||
members: {
|
||||
name: 'dialog.user.groups.sorting.members',
|
||||
value: 'members'
|
||||
},
|
||||
inGame: {
|
||||
name: 'dialog.user.groups.sorting.in_game',
|
||||
value: 'inGame'
|
||||
}
|
||||
};
|
||||
|
||||
export { userDialogGroupSortingOptions };
|
||||
79
src/composables/user/utils.js
Normal file
79
src/composables/user/utils.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { languageMappings } from './constants/language';
|
||||
|
||||
function userOnlineForTimestamp(ctx) {
|
||||
if (ctx.ref.state === 'online' && ctx.ref.$online_for) {
|
||||
return ctx.ref.$online_for;
|
||||
} else if (ctx.ref.state === 'active' && ctx.ref.$active_for) {
|
||||
return ctx.ref.$active_for;
|
||||
} else if (ctx.ref.$offline_for) {
|
||||
return ctx.ref.$offline_for;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function languageClass(language) {
|
||||
const style = {};
|
||||
const mapping = languageMappings[language];
|
||||
if (typeof mapping !== 'undefined') {
|
||||
style[mapping] = true;
|
||||
} else {
|
||||
style.unknown = true;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
function getPrintFileName(print) {
|
||||
const authorName = print.authorName;
|
||||
// fileDate format: 2024-11-03_16-14-25.757
|
||||
const createdAt = getPrintLocalDate(print);
|
||||
const fileNameDate = createdAt
|
||||
.toISOString()
|
||||
.replace(/:/g, '-')
|
||||
.replace(/T/g, '_')
|
||||
.replace(/Z/g, '');
|
||||
const fileName = `${authorName}_${fileNameDate}_${print.id}.png`;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function getPrintLocalDate(print) {
|
||||
if (print.createdAt) {
|
||||
const createdAt = new Date(print.createdAt);
|
||||
// cursed convert to local time
|
||||
createdAt.setMinutes(
|
||||
createdAt.getMinutes() - createdAt.getTimezoneOffset()
|
||||
);
|
||||
return createdAt;
|
||||
}
|
||||
if (print.timestamp) {
|
||||
return new Date(print.timestamp);
|
||||
}
|
||||
|
||||
const createdAt = new Date();
|
||||
// cursed convert to local time
|
||||
createdAt.setMinutes(
|
||||
createdAt.getMinutes() - createdAt.getTimezoneOffset()
|
||||
);
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
function isFriendOnline(friend) {
|
||||
if (typeof friend === 'undefined' || typeof friend.ref === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (friend.state === 'online') {
|
||||
return true;
|
||||
}
|
||||
if (friend.state !== 'online' && friend.ref.location !== 'private') {
|
||||
// wat
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export {
|
||||
userOnlineForTimestamp,
|
||||
languageClass,
|
||||
getPrintFileName,
|
||||
getPrintLocalDate,
|
||||
isFriendOnline
|
||||
};
|
||||
Reference in New Issue
Block a user