mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-18 22:33:50 +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:
109
src/App.vue
Normal file
109
src/App.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script>
|
||||
import Vue, { onMounted } from 'vue';
|
||||
import template from './app.pug';
|
||||
import { createGlobalStores } from './stores';
|
||||
import { watchState } from './service/watchState';
|
||||
|
||||
import Login from './views/Login/Login.vue';
|
||||
import NavMenu from './components/NavMenu.vue';
|
||||
import Sidebar from './views/Sidebar/Sidebar.vue';
|
||||
import Feed from './views/Feed/Feed.vue';
|
||||
import GameLog from './views/GameLog/GameLog.vue';
|
||||
import PlayerList from './views/PlayerList/PlayerList.vue';
|
||||
import Search from './views/Search/Search.vue';
|
||||
import Favorites from './views/Favorites/Favorites.vue';
|
||||
import FriendLog from './views/FriendLog/FriendLog.vue';
|
||||
import Moderation from './views/Moderation/Moderation.vue';
|
||||
import Notification from './views/Notifications/Notification.vue';
|
||||
import FriendList from './views/FriendList/FriendList.vue';
|
||||
import Charts from './views/Charts/Charts.vue';
|
||||
import Profile from './views/Profile/Profile.vue';
|
||||
import Settings from './views/Settings/Settings.vue';
|
||||
|
||||
import UserDialog from './components/dialogs/UserDialog/UserDialog.vue';
|
||||
import WorldDialog from './components/dialogs/WorldDialog/WorldDialog.vue';
|
||||
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
|
||||
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
|
||||
import GalleryDialog from './components/dialogs/GalleryDialog.vue';
|
||||
import FullscreenImageDialog from './components/dialogs/FullscreenImageDialog.vue';
|
||||
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
|
||||
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
|
||||
import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue';
|
||||
import FriendImportDialog from './views/Favorites/dialogs/FriendImportDialog.vue';
|
||||
import WorldImportDialog from './views/Favorites/dialogs/WorldImportDialog.vue';
|
||||
import AvatarImportDialog from './views/Favorites/dialogs/AvatarImportDialog.vue';
|
||||
import ChooseFavoriteGroupDialog from './components/dialogs/ChooseFavoriteGroupDialog.vue';
|
||||
import EditInviteMessageDialog from './views/Profile/dialogs/EditInviteMessageDialog.vue';
|
||||
import VRCXUpdateDialog from './components/dialogs/VRCXUpdateDialog.vue';
|
||||
import VRChatConfigDialog from './views/Settings/dialogs/VRChatConfigDialog.vue';
|
||||
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
|
||||
|
||||
import { utils } from './shared/utils/_utils';
|
||||
|
||||
export default {
|
||||
template,
|
||||
components: {
|
||||
Login,
|
||||
NavMenu,
|
||||
Sidebar,
|
||||
Feed,
|
||||
GameLog,
|
||||
PlayerList,
|
||||
Search,
|
||||
Favorites,
|
||||
FriendLog,
|
||||
Moderation,
|
||||
Notification,
|
||||
FriendList,
|
||||
Charts,
|
||||
Profile,
|
||||
Settings,
|
||||
|
||||
UserDialog,
|
||||
WorldDialog,
|
||||
AvatarDialog,
|
||||
GroupDialog,
|
||||
GalleryDialog,
|
||||
FullscreenImageDialog,
|
||||
PreviousInstancesInfoDialog,
|
||||
LaunchDialog,
|
||||
LaunchOptionsDialog,
|
||||
FriendImportDialog,
|
||||
WorldImportDialog,
|
||||
AvatarImportDialog,
|
||||
ChooseFavoriteGroupDialog,
|
||||
EditInviteMessageDialog,
|
||||
VRCXUpdateDialog,
|
||||
VRChatConfigDialog,
|
||||
PrimaryPasswordDialog
|
||||
},
|
||||
setup() {
|
||||
const store = createGlobalStores();
|
||||
Vue.prototype.store = store;
|
||||
Vue.prototype.utils = utils;
|
||||
|
||||
store.updateLoop.updateLoop();
|
||||
|
||||
onMounted(async () => {
|
||||
store.gameLog.getGameLogTable();
|
||||
await store.auth.migrateStoredUsers();
|
||||
store.auth.autoLoginAfterMounted();
|
||||
store.vrcx.checkAutoBackupRestoreVrcRegistry();
|
||||
store.game.checkVRChatDebugLogging();
|
||||
try {
|
||||
if (await AppApi.IsRunningUnderWine()) {
|
||||
store.vrcx.isRunningUnderWine = true;
|
||||
store.vrcx.applyWineEmojis();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, 'Failed to check if running under Wine');
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
store,
|
||||
watchState
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
69
src/api/auth.js
Normal file
69
src/api/auth.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
const loginReq = {
|
||||
/**
|
||||
* @param {{ code: string }} params One-time password
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
verifyOTP(params) {
|
||||
return request('auth/twofactorauth/otp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
verifyTOTP(params) {
|
||||
return request('auth/twofactorauth/totp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
verifyEmailOTP(params) {
|
||||
return request('auth/twofactorauth/emailotp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getConfig() {
|
||||
return request('config', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
useUserStore().handleConfig(args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default loginReq;
|
||||
@@ -1,19 +1,18 @@
|
||||
// #region | API: Avatar
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
const avatarReq = {
|
||||
/**
|
||||
* @param {{ avatarId: string }} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
* @type {import('../types/avatar').getAvatar}
|
||||
*/
|
||||
getAvatar(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}`, {
|
||||
return request(`avatars/${params.avatarId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -37,7 +36,7 @@ const avatarReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getAvatars(params) {
|
||||
return window.API.call('avatars', {
|
||||
return request('avatars', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -45,16 +44,16 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR:LIST', args);
|
||||
|
||||
return args;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {{ id: string, releaseStatus: 'public' | 'private' }} params
|
||||
* @param {{ id: string, releaseStatus?: 'public' | 'private', name?: string, description?: string }} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
saveAvatar(params) {
|
||||
return window.API.call(`avatars/${params.id}`, {
|
||||
return request(`avatars/${params.id}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -62,7 +61,6 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR:SAVE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -72,7 +70,8 @@ const avatarReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
selectAvatar(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}/select`, {
|
||||
const userStore = useUserStore();
|
||||
return request(`avatars/${params.avatarId}/select`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -80,7 +79,7 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR:SELECT', args);
|
||||
userStore.applyCurrentUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -90,7 +89,8 @@ const avatarReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
selectFallbackAvatar(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}/selectfallback`, {
|
||||
const userStore = useUserStore();
|
||||
return request(`avatars/${params.avatarId}/selectfallback`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -98,7 +98,7 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR:SELECT', args);
|
||||
userStore.applyCurrentUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -108,14 +108,13 @@ const avatarReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
deleteAvatar(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}`, {
|
||||
return request(`avatars/${params.avatarId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -125,14 +124,13 @@ const avatarReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
createImposter(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}/impostor/enqueue`, {
|
||||
return request(`avatars/${params.avatarId}/impostor/enqueue`, {
|
||||
method: 'POST'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATAR:IMPOSTER:CREATE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -142,35 +140,33 @@ const avatarReq = {
|
||||
* @returns {Promise<{json: T, params}>}
|
||||
*/
|
||||
deleteImposter(params) {
|
||||
return window.API.call(`avatars/${params.avatarId}/impostor`, {
|
||||
return request(`avatars/${params.avatarId}/impostor`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATAR:IMPOSTER:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
* @returns {Promise<{json: any}>}
|
||||
*/
|
||||
getAvailableAvatarStyles() {
|
||||
return window.API.call('avatarStyles', {
|
||||
return request('avatarStyles', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
// window.API.$emit('AVATAR:STYLES', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{ avatarId: string }} params
|
||||
* @param {{ avatarId: string }} avatarId
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getAvatarGallery(avatarId) {
|
||||
@@ -180,7 +176,7 @@ const avatarReq = {
|
||||
n: 100,
|
||||
offset: 0
|
||||
};
|
||||
return window.API.call(`files`, {
|
||||
return request(`files`, {
|
||||
params,
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
@@ -188,7 +184,6 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATAR:GALLERY', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -202,7 +197,7 @@ const avatarReq = {
|
||||
tag: 'avatargallery',
|
||||
galleryId: avatarId
|
||||
};
|
||||
return window.API.call('file/image', {
|
||||
return request('file/image', {
|
||||
uploadImage: true,
|
||||
matchingDimensions: false,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -212,7 +207,6 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARGALLERYIMAGE:ADD', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -225,7 +219,7 @@ const avatarReq = {
|
||||
const params = {
|
||||
ids: order
|
||||
};
|
||||
return window.API.call('files/order', {
|
||||
return request('files/order', {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -233,11 +227,9 @@ const avatarReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARGALLERYIMAGE:ORDER', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
export default avatarReq;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// #region | API: AvatarModeration
|
||||
import { request } from '../service/request';
|
||||
|
||||
const avatarModerationReq = {
|
||||
getAvatarModerations() {
|
||||
return window.API.call('auth/user/avatarmoderations', {
|
||||
return request('auth/user/avatarmoderations', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
// window.API.$emit('AVATAR-MODERATION:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -18,7 +17,7 @@ const avatarModerationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
sendAvatarModeration(params) {
|
||||
return window.API.call('auth/user/avatarmoderations', {
|
||||
return request('auth/user/avatarmoderations', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -26,7 +25,6 @@ const avatarModerationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATAR-MODERATION', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -36,7 +34,7 @@ const avatarModerationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
deleteAvatarModeration(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`auth/user/avatarmoderations?targetAvatarId=${encodeURIComponent(
|
||||
params.targetAvatarId
|
||||
)}&avatarModerationType=${encodeURIComponent(
|
||||
@@ -50,11 +48,9 @@ const avatarModerationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('AVATAR-MODERATION:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
export default avatarModerationReq;
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
// #region | API: Favorite
|
||||
import { request } from '../service/request';
|
||||
import { useFavoriteStore, useUserStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
|
||||
const favoriteReq = {
|
||||
getFavoriteLimits() {
|
||||
return window.API.call('auth/user/favoritelimits', {
|
||||
return request('auth/user/favoritelimits', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
window.API.$emit('FAVORITE:LIMITS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* n: number,
|
||||
* offset: number,
|
||||
* type: string,
|
||||
* tag: string
|
||||
* }} params
|
||||
* @return { Promise<{json: any, params}> }
|
||||
* @type {import('../types/favorite').getFavorites}
|
||||
*/
|
||||
getFavorites(params) {
|
||||
return window.API.call('favorites', {
|
||||
return request('favorites', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -31,7 +29,6 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -45,7 +42,7 @@ const favoriteReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
addFavorite(params) {
|
||||
return window.API.call('favorites', {
|
||||
return request('favorites', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -53,7 +50,7 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:ADD', args);
|
||||
useFavoriteStore().handleFavoriteAdd(args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -63,14 +60,14 @@ const favoriteReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
deleteFavorite(params) {
|
||||
return window.API.call(`favorites/${params.objectId}`, {
|
||||
return request(`favorites/${params.objectId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:DELETE', args);
|
||||
useFavoriteStore().handleFavoriteDelete(args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -80,7 +77,7 @@ const favoriteReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getFavoriteGroups(params) {
|
||||
return window.API.call('favorite/groups', {
|
||||
return request('favorite/groups', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -88,7 +85,6 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:GROUP:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -99,8 +95,8 @@ const favoriteReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
saveFavoriteGroup(params) {
|
||||
return window.API.call(
|
||||
`favorite/group/${params.type}/${params.group}/${window.API.currentUser.id}`,
|
||||
return request(
|
||||
`favorite/group/${params.type}/${params.group}/${getCurrentUserId()}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params
|
||||
@@ -110,7 +106,6 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:GROUP:SAVE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -123,8 +118,8 @@ const favoriteReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
clearFavoriteGroup(params) {
|
||||
return window.API.call(
|
||||
`favorite/group/${params.type}/${params.group}/${window.API.currentUser.id}`,
|
||||
return request(
|
||||
`favorite/group/${params.type}/${params.group}/${getCurrentUserId()}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
params
|
||||
@@ -134,20 +129,16 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:GROUP:CLEAR', args);
|
||||
useFavoriteStore().handleFavoriteGroupClear(args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* n: number,
|
||||
* offset: number
|
||||
* }} params
|
||||
* @return { Promise<{json: any, params}> }
|
||||
* @type {import('../types/favorite').getFavoriteWorlds}
|
||||
*/
|
||||
getFavoriteWorlds(params) {
|
||||
return window.API.call('worlds/favorites', {
|
||||
return request('worlds/favorites', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -155,20 +146,15 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:WORLD:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* n: number,
|
||||
* offset: number
|
||||
* }} params
|
||||
* @return { Promise<{json: any, params}> }
|
||||
* @type {import('../types/favorite').getFavoriteAvatars}
|
||||
*/
|
||||
getFavoriteAvatars(params) {
|
||||
return window.API.call('avatars/favorites', {
|
||||
return request('avatars/favorites', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -176,12 +162,9 @@ const favoriteReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FAVORITE:AVATAR:LIST', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
export default favoriteReq;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// #region | API: Friend
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores/user';
|
||||
|
||||
const friendReq = {
|
||||
/**
|
||||
* Fetch friends of current user.
|
||||
* @param {{ n: number, offset: number, offline: boolean }} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
* @type {import('../types/friend').getFriends}
|
||||
*/
|
||||
getFriends(params) {
|
||||
return window.API.call('auth/user/friends', {
|
||||
const userStore = useUserStore();
|
||||
return request('auth/user/friends', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -15,7 +16,13 @@ const friendReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FRIEND:LIST', args);
|
||||
for (const user of args.json) {
|
||||
if (!user.displayName) {
|
||||
console.error('/friends gave us garbage', user);
|
||||
continue;
|
||||
}
|
||||
userStore.applyUser(user);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -25,14 +32,13 @@ const friendReq = {
|
||||
* @returns {Promise<{json: T, params}>}
|
||||
*/
|
||||
sendFriendRequest(params) {
|
||||
return window.API.call(`user/${params.userId}/friendRequest`, {
|
||||
return request(`user/${params.userId}/friendRequest`, {
|
||||
method: 'POST'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FRIEND:REQUEST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -42,14 +48,13 @@ const friendReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
cancelFriendRequest(params) {
|
||||
return window.API.call(`user/${params.userId}/friendRequest`, {
|
||||
return request(`user/${params.userId}/friendRequest`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('FRIEND:REQUEST:CANCEL', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -59,14 +64,13 @@ const friendReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
deleteFriend(params) {
|
||||
return window.API.call(`auth/user/friends/${params.userId}`, {
|
||||
return request(`auth/user/friends/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FRIEND:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -76,7 +80,7 @@ const friendReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getFriendStatus(params) {
|
||||
return window.API.call(`user/${params.userId}/friendStatus`, {
|
||||
return request(`user/${params.userId}/friendStatus`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
console.log('getFriendStatus', json);
|
||||
@@ -84,15 +88,12 @@ const friendReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FRIEND:STATUS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
// ------------------- need to test -------------------
|
||||
|
||||
deleteHiddenFriendRequest(params, userId) {
|
||||
return window.API.call(`user/${userId}/friendRequest`, {
|
||||
return request(`user/${userId}/friendRequest`, {
|
||||
method: 'DELETE',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -101,11 +102,9 @@ const friendReq = {
|
||||
params,
|
||||
userId
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:HIDE', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
export default friendReq;
|
||||
|
||||
262
src/api/group.js
262
src/api/group.js
@@ -1,3 +1,9 @@
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore, useGroupStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
const groupReq = {
|
||||
/**
|
||||
* @param {string} groupId
|
||||
@@ -5,7 +11,7 @@ const groupReq = {
|
||||
* @returns
|
||||
*/
|
||||
setGroupRepresentation(groupId, params) {
|
||||
return window.API.call(`groups/${groupId}/representation`, {
|
||||
return request(`groups/${groupId}/representation`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -23,7 +29,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
cancelGroupRequest(params) {
|
||||
return window.API.call(`groups/${params.groupId}/requests`, {
|
||||
return request(`groups/${params.groupId}/requests`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -39,12 +45,9 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
deleteGroupPost(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/posts/${params.postId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/posts/${params.postId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
@@ -53,10 +56,10 @@ const groupReq = {
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {{ groupId: string }} params
|
||||
* @type {import('../types/group').getGroup}
|
||||
*/
|
||||
getGroup(params) {
|
||||
return window.API.call(`groups/${params.groupId}`, {
|
||||
return request(`groups/${params.groupId}`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
includeRoles: params.includeRoles || false
|
||||
@@ -66,23 +69,48 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {{ groupId: string }} params
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getCachedGroup(params) {
|
||||
const groupStore = useGroupStore();
|
||||
return new Promise((resolve, reject) => {
|
||||
const ref = groupStore.cachedGroups.get(params.groupId);
|
||||
if (typeof ref === 'undefined') {
|
||||
groupReq
|
||||
.getGroup(params)
|
||||
.catch(reject)
|
||||
.then((args) => {
|
||||
args.ref = groupStore.applyGroup(args.json);
|
||||
resolve(args);
|
||||
});
|
||||
} else {
|
||||
resolve({
|
||||
cache: true,
|
||||
json: ref,
|
||||
params,
|
||||
ref
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {{ userId: string }} params
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getRepresentedGroup(params) {
|
||||
return window.API.call(`users/${params.userId}/groups/represented`, {
|
||||
return request(`users/${params.userId}/groups/represented`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:REPRESENTED', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -91,14 +119,13 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroups(params) {
|
||||
return window.API.call(`users/${params.userId}/groups`, {
|
||||
return request(`users/${params.userId}/groups`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -107,14 +134,13 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
joinGroup(params) {
|
||||
return window.API.call(`groups/${params.groupId}/join`, {
|
||||
return request(`groups/${params.groupId}/join`, {
|
||||
method: 'POST'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:JOIN', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -123,7 +149,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
leaveGroup(params) {
|
||||
return window.API.call(`groups/${params.groupId}/leave`, {
|
||||
return request(`groups/${params.groupId}/leave`, {
|
||||
method: 'POST'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -138,7 +164,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
groupStrictsearch(params) {
|
||||
return window.API.call(`groups/strictsearch`, {
|
||||
return request(`groups/strictsearch`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -159,7 +185,7 @@ const groupReq = {
|
||||
}
|
||||
*/
|
||||
setGroupMemberProps(userId, groupId, params) {
|
||||
return window.API.call(`groups/${groupId}/members/${userId}`, {
|
||||
return request(`groups/${groupId}/members/${userId}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -169,7 +195,6 @@ const groupReq = {
|
||||
groupId,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:MEMBER:PROPS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -182,7 +207,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
addGroupMemberRole(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`groups/${params.groupId}/members/${params.userId}/roles/${params.roleId}`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -192,7 +217,6 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('GROUP:MEMBER:ROLE:CHANGE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -205,7 +229,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
removeGroupMemberRole(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`groups/${params.groupId}/members/${params.userId}/roles/${params.roleId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
@@ -215,19 +239,17 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('GROUP:MEMBER:ROLE:CHANGE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
getGroupPermissions(params) {
|
||||
return window.API.call(`users/${params.userId}/groups/permissions`, {
|
||||
return request(`users/${params.userId}/groups/permissions`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:PERMISSIONS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -240,7 +262,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupPosts(params) {
|
||||
return window.API.call(`groups/${params.groupId}/posts`, {
|
||||
return request(`groups/${params.groupId}/posts`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -248,28 +270,23 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:POSTS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
editGroupPost(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/posts/${params.postId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/posts/${params.postId}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:POST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
createGroupPost(params) {
|
||||
return window.API.call(`groups/${params.groupId}/posts`, {
|
||||
return request(`groups/${params.groupId}/posts`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -277,7 +294,6 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:POST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -289,17 +305,13 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupMember(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/members/${params.userId}`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/members/${params.userId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('GROUP:MEMBER', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -312,7 +324,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupMembers(params) {
|
||||
return window.API.call(`groups/${params.groupId}/members`, {
|
||||
return request(`groups/${params.groupId}/members`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -320,7 +332,6 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:MEMBERS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -334,7 +345,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupMembersSearch(params) {
|
||||
return window.API.call(`groups/${params.groupId}/members/search`, {
|
||||
return request(`groups/${params.groupId}/members/search`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -352,7 +363,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
blockGroup(params) {
|
||||
return window.API.call(`groups/${params.groupId}/block`, {
|
||||
return request(`groups/${params.groupId}/block`, {
|
||||
method: 'POST'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -370,12 +381,9 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
unblockGroup(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/members/${params.userId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/members/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
@@ -391,7 +399,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
sendGroupInvite(params) {
|
||||
return window.API.call(`groups/${params.groupId}/invites`, {
|
||||
return request(`groups/${params.groupId}/invites`, {
|
||||
method: 'POST',
|
||||
params: {
|
||||
userId: params.userId
|
||||
@@ -401,7 +409,6 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GROUP:INVITE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -413,18 +420,13 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
kickGroupMember(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/members/${params.userId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/members/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:MEMBER:KICK', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -433,7 +435,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
banGroupMember(params) {
|
||||
return window.API.call(`groups/${params.groupId}/bans`, {
|
||||
return request(`groups/${params.groupId}/bans`, {
|
||||
method: 'POST',
|
||||
params: {
|
||||
userId: params.userId
|
||||
@@ -443,24 +445,17 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:MEMBER:BAN', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
unbanGroupMember(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/bans/${params.userId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/bans/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:MEMBER:UNBAN', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -469,97 +464,73 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
deleteSentGroupInvite(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/invites/${params.userId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/invites/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:INVITE:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
deleteBlockedGroupRequest(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/members/${params.userId}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`groups/${params.groupId}/members/${params.userId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:BLOCKED:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
acceptGroupInviteRequest(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/requests/${params.userId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'accept'
|
||||
}
|
||||
return request(`groups/${params.groupId}/requests/${params.userId}`, {
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'accept'
|
||||
}
|
||||
).then((json) => {
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:INVITE:ACCEPT', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
rejectGroupInviteRequest(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/requests/${params.userId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'reject'
|
||||
}
|
||||
return request(`groups/${params.groupId}/requests/${params.userId}`, {
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'reject'
|
||||
}
|
||||
).then((json) => {
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:INVITE:REJECT', args);
|
||||
|
||||
return args;
|
||||
});
|
||||
},
|
||||
blockGroupInviteRequest(params) {
|
||||
return window.API.call(
|
||||
`groups/${params.groupId}/requests/${params.userId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'reject',
|
||||
block: true
|
||||
}
|
||||
return request(`groups/${params.groupId}/requests/${params.userId}`, {
|
||||
method: 'PUT',
|
||||
params: {
|
||||
action: 'reject',
|
||||
block: true
|
||||
}
|
||||
).then((json) => {
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// window.API.$emit('GROUP:INVITE:BLOCK', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
getGroupBans(params) {
|
||||
return window.API.call(`groups/${params.groupId}/bans`, {
|
||||
return request(`groups/${params.groupId}/bans`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -575,7 +546,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupAuditLogTypes(params) {
|
||||
return window.API.call(`groups/${params.groupId}/auditLogTypes`, {
|
||||
return request(`groups/${params.groupId}/auditLogTypes`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -590,7 +561,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupLogs(params) {
|
||||
return window.API.call(`groups/${params.groupId}/auditLogs`, {
|
||||
return request(`groups/${params.groupId}/auditLogs`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -606,7 +577,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupInvites(params) {
|
||||
return window.API.call(`groups/${params.groupId}/invites`, {
|
||||
return request(`groups/${params.groupId}/invites`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -622,7 +593,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupJoinRequests(params) {
|
||||
return window.API.call(`groups/${params.groupId}/requests`, {
|
||||
return request(`groups/${params.groupId}/requests`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -630,7 +601,6 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('GROUP:JOINREQUESTS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -639,8 +609,8 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupInstances(params) {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/instances/groups/${params.groupId}`,
|
||||
return request(
|
||||
`users/${getCurrentUserId()}/instances/groups/${params.groupId}`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
@@ -657,7 +627,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupRoles(params) {
|
||||
return window.API.call(`groups/${params.groupId}/roles`, {
|
||||
return request(`groups/${params.groupId}/roles`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -665,22 +635,16 @@ const groupReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// useless code
|
||||
// this.$emit('GROUP:ROLES', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
getUsersGroupInstances() {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/instances/groups`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`users/${getCurrentUserId()}/instances/groups`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
window.API.$emit('GROUP:USER:INSTANCES', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -696,7 +660,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
groupSearch(params) {
|
||||
return window.API.call(`groups`, {
|
||||
return request(`groups`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -717,7 +681,7 @@ const groupReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getGroupGallery(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`groups/${params.groupId}/galleries/${params.galleryId}`,
|
||||
{
|
||||
method: 'GET',
|
||||
@@ -735,10 +699,9 @@ const groupReq = {
|
||||
});
|
||||
}
|
||||
|
||||
// no place to use this
|
||||
// getRequestedGroups() {
|
||||
// return window.API.call(
|
||||
// `users/${window.API.currentUser.id}/groups/requested`,
|
||||
// return request(
|
||||
// `users/${API.currentUser.id}/groups/requested`,
|
||||
// {
|
||||
// method: 'GET'
|
||||
// }
|
||||
@@ -746,18 +709,17 @@ const groupReq = {
|
||||
// const args = {
|
||||
// json
|
||||
// };
|
||||
// window.API.$emit('GROUP:REQUESTED', args);
|
||||
// API.$emit('GROUP:REQUESTED', args);
|
||||
// return args;
|
||||
// });
|
||||
// }
|
||||
|
||||
// ----------------- left over code -----------------
|
||||
// /**
|
||||
// * @param {{ groupId: string }} params
|
||||
// * @return { Promise<{json: any, params}> }
|
||||
// */
|
||||
// API.getGroupAnnouncement = function (params) {
|
||||
// return this.call(`groups/${params.groupId}/announcement`, {
|
||||
// return request(`groups/${params.groupId}/announcement`, {
|
||||
// method: 'GET'
|
||||
// }).then((json) => {
|
||||
// var args = {
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import { request } from '../service/request';
|
||||
import { useAvatarStore, useWorldStore } from '../stores';
|
||||
|
||||
const imageReq = {
|
||||
// use in uploadAvatarImage
|
||||
// need to test
|
||||
async uploadAvatarFailCleanup(id) {
|
||||
const json = await window.API.call(`file/${id}`, {
|
||||
const avatarStore = useAvatarStore();
|
||||
const json = await request(`file/${id}`, {
|
||||
method: 'GET'
|
||||
});
|
||||
const fileId = json.id;
|
||||
const fileVersion = json.versions[json.versions.length - 1].version;
|
||||
window.API.call(`file/${fileId}/${fileVersion}/signature/finish`, {
|
||||
request(`file/${fileId}/${fileVersion}/signature/finish`, {
|
||||
method: 'PUT'
|
||||
});
|
||||
window.API.call(`file/${fileId}/${fileVersion}/file/finish`, {
|
||||
request(`file/${fileId}/${fileVersion}/file/finish`, {
|
||||
method: 'PUT'
|
||||
});
|
||||
window.$app.avatarDialog.loading = false;
|
||||
// window.$app.changeAvatarImageDialogLoading = false;
|
||||
avatarStore.avatarDialog.loading = false;
|
||||
},
|
||||
|
||||
async uploadAvatarImage(params, fileId) {
|
||||
try {
|
||||
return await window.API.call(`file/${fileId}`, {
|
||||
return await request(`file/${fileId}`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -28,7 +29,6 @@ const imageReq = {
|
||||
params,
|
||||
fileId
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:INIT', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -36,12 +36,11 @@ const imageReq = {
|
||||
imageReq.uploadAvatarFailCleanup(fileId);
|
||||
throw err;
|
||||
}
|
||||
// return void 0;
|
||||
},
|
||||
|
||||
async uploadAvatarImageFileStart(params) {
|
||||
try {
|
||||
return await window.API.call(
|
||||
return await request(
|
||||
`file/${params.fileId}/${params.fileVersion}/file/start`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -51,18 +50,16 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:FILESTART', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
imageReq.uploadAvatarFailCleanup(params.fileId);
|
||||
}
|
||||
return void 0;
|
||||
},
|
||||
|
||||
uploadAvatarImageFileFinish(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`file/${params.fileId}/${params.fileVersion}/file/finish`,
|
||||
{
|
||||
method: 'PUT',
|
||||
@@ -76,14 +73,13 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:FILEFINISH', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
async uploadAvatarImageSigStart(params) {
|
||||
try {
|
||||
return await window.API.call(
|
||||
return await request(
|
||||
`file/${params.fileId}/${params.fileVersion}/signature/start`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -93,18 +89,16 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:SIGSTART', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
imageReq.uploadAvatarFailCleanup(params.fileId);
|
||||
}
|
||||
return void 0;
|
||||
},
|
||||
|
||||
uploadAvatarImageSigFinish(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`file/${params.fileId}/${params.fileVersion}/signature/finish`,
|
||||
{
|
||||
method: 'PUT',
|
||||
@@ -118,13 +112,12 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:SIGFINISH', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
setAvatarImage(params) {
|
||||
return window.API.call(`avatars/${params.id}`, {
|
||||
return request(`avatars/${params.id}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -132,33 +125,29 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:SET', args);
|
||||
window.API.$emit('AVATAR', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
// use in uploadWorldImage
|
||||
// need to test
|
||||
async uploadWorldFailCleanup(id) {
|
||||
const json = await window.API.call(`file/${id}`, {
|
||||
const worldStore = useWorldStore();
|
||||
const json = await request(`file/${id}`, {
|
||||
method: 'GET'
|
||||
});
|
||||
const fileId = json.id;
|
||||
const fileVersion = json.versions[json.versions.length - 1].version;
|
||||
window.API.call(`file/${fileId}/${fileVersion}/signature/finish`, {
|
||||
request(`file/${fileId}/${fileVersion}/signature/finish`, {
|
||||
method: 'PUT'
|
||||
});
|
||||
window.API.call(`file/${fileId}/${fileVersion}/file/finish`, {
|
||||
request(`file/${fileId}/${fileVersion}/file/finish`, {
|
||||
method: 'PUT'
|
||||
});
|
||||
window.$app.worldDialog.loading = false;
|
||||
// window.$app.changeWorldImageDialogLoading = false;
|
||||
worldStore.worldDialog.loading = false;
|
||||
},
|
||||
|
||||
async uploadWorldImage(params, fileId) {
|
||||
try {
|
||||
return await window.API.call(`file/${fileId}`, {
|
||||
return await request(`file/${fileId}`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -167,7 +156,6 @@ const imageReq = {
|
||||
params,
|
||||
fileId
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:INIT', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -179,7 +167,7 @@ const imageReq = {
|
||||
|
||||
async uploadWorldImageFileStart(params) {
|
||||
try {
|
||||
return await window.API.call(
|
||||
return await request(
|
||||
`file/${params.fileId}/${params.fileVersion}/file/start`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -189,7 +177,6 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:FILESTART', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -200,7 +187,7 @@ const imageReq = {
|
||||
},
|
||||
|
||||
uploadWorldImageFileFinish(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`file/${params.fileId}/${params.fileVersion}/file/finish`,
|
||||
{
|
||||
method: 'PUT',
|
||||
@@ -214,14 +201,13 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:FILEFINISH', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
async uploadWorldImageSigStart(params) {
|
||||
try {
|
||||
return await window.API.call(
|
||||
return await request(
|
||||
`file/${params.fileId}/${params.fileVersion}/signature/start`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -231,7 +217,6 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:SIGSTART', args);
|
||||
return args;
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -242,7 +227,7 @@ const imageReq = {
|
||||
},
|
||||
|
||||
uploadWorldImageSigFinish(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`file/${params.fileId}/${params.fileVersion}/signature/finish`,
|
||||
{
|
||||
method: 'PUT',
|
||||
@@ -256,13 +241,13 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:SIGFINISH', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
setWorldImage(params) {
|
||||
return window.API.call(`worlds/${params.id}`, {
|
||||
const worldStore = useWorldStore();
|
||||
return request(`worlds/${params.id}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -270,27 +255,25 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:SET', args);
|
||||
window.API.$emit('WORLD', args);
|
||||
args.ref = worldStore.applyWorld(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getAvatarImages(params) {
|
||||
return window.API.call(`file/${params.fileId}`, {
|
||||
return request(`file/${params.fileId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('AVATARIMAGE:GET', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getWorldImages(params) {
|
||||
return window.API.call(`file/${params.fileId}`, {
|
||||
return request(`file/${params.fileId}`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -298,7 +281,6 @@ const imageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('WORLDIMAGE:GET', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/**
|
||||
* API requests
|
||||
* Export all API requests from here
|
||||
*
|
||||
* "window.API" is used as app.js is a large IIFE, preventing direct API export. No current issues
|
||||
* Refactoring may be required
|
||||
*/
|
||||
|
||||
import userRequest from './user';
|
||||
@@ -21,6 +18,7 @@ import inviteMessagesRequest from './inviteMessages';
|
||||
import imageRequest from './image';
|
||||
import miscRequest from './misc';
|
||||
import groupRequest from './group';
|
||||
import authRequest from './auth';
|
||||
import inventoryRequest from './inventory';
|
||||
import propRequest from './prop';
|
||||
|
||||
@@ -39,6 +37,7 @@ window.request = {
|
||||
inviteMessagesRequest,
|
||||
imageRequest,
|
||||
miscRequest,
|
||||
authRequest,
|
||||
groupRequest,
|
||||
inventoryRequest,
|
||||
propRequest
|
||||
@@ -59,6 +58,7 @@ export {
|
||||
inviteMessagesRequest,
|
||||
imageRequest,
|
||||
miscRequest,
|
||||
authRequest,
|
||||
groupRequest,
|
||||
inventoryRequest,
|
||||
propRequest
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
// #region | API: Instance
|
||||
import { $app } from '../app';
|
||||
import { t } from '../plugin';
|
||||
import { request } from '../service/request';
|
||||
import { useInstanceStore } from '../stores';
|
||||
|
||||
const instanceReq = {
|
||||
/**
|
||||
* @param {{worldId: string, instanceId: string}} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
* @type {import('../types/instance').getInstance}
|
||||
*/
|
||||
getInstance(params) {
|
||||
return window.API.call(
|
||||
`instances/${params.worldId}:${params.instanceId}`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
).then((json) => {
|
||||
const instanceStore = useInstanceStore();
|
||||
return request(`instances/${params.worldId}:${params.instanceId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('INSTANCE', args);
|
||||
args.ref = instanceStore.applyInstance(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -37,7 +37,8 @@ const instanceReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
createInstance(params) {
|
||||
return window.API.call('instances', {
|
||||
const instanceStore = useInstanceStore();
|
||||
return request('instances', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -45,7 +46,7 @@ const instanceReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('INSTANCE', args);
|
||||
args.ref = instanceStore.applyInstance(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -59,7 +60,7 @@ const instanceReq = {
|
||||
if (instance.shortName) {
|
||||
params.shortName = instance.shortName;
|
||||
}
|
||||
return window.API.call(
|
||||
return request(
|
||||
`instances/${instance.worldId}:${instance.instanceId}/shortName`,
|
||||
{
|
||||
method: 'GET',
|
||||
@@ -80,14 +81,15 @@ const instanceReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getInstanceFromShortName(params) {
|
||||
return window.API.call(`instances/s/${params.shortName}`, {
|
||||
const instanceStore = useInstanceStore();
|
||||
return request(`instances/s/${params.shortName}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('INSTANCE', args);
|
||||
args.ref = instanceStore.applyInstance(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -105,7 +107,7 @@ const instanceReq = {
|
||||
if (instance.shortName) {
|
||||
params.shortName = instance.shortName;
|
||||
}
|
||||
return window.API.call(
|
||||
return request(
|
||||
`invite/myself/to/${instance.worldId}:${instance.instanceId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
@@ -121,20 +123,19 @@ const instanceReq = {
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err?.error?.message) {
|
||||
window.$app.$message({
|
||||
$app.$message({
|
||||
message: err.error.message,
|
||||
type: 'error'
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
window.$app.$message({
|
||||
message: window.$t('message.instance.not_allowed'),
|
||||
$app.$message({
|
||||
message: t('message.instance.not_allowed'),
|
||||
type: 'error'
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
export default instanceReq;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { request } from '../service/request';
|
||||
|
||||
const inventoryReq = {
|
||||
/**
|
||||
* @param {{ inventoryId: string, userId: string }} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getUserInventoryItem(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`user/${params.userId}/inventory/${params.inventoryId}`,
|
||||
{
|
||||
method: 'GET'
|
||||
@@ -23,7 +25,7 @@ const inventoryReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getInventoryItem(params) {
|
||||
return window.API.call(`inventory/${params.inventoryId}`, {
|
||||
return request(`inventory/${params.inventoryId}`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -40,7 +42,7 @@ const inventoryReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getInventoryItems(params) {
|
||||
return window.API.call('inventory', {
|
||||
return request('inventory', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -57,7 +59,7 @@ const inventoryReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
consumeInventoryBundle(params) {
|
||||
return window.API.call(`inventory/${params.inventoryId}/consume`, {
|
||||
return request(`inventory/${params.inventoryId}/consume`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -74,13 +76,10 @@ const inventoryReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getInventoryTemplate(params) {
|
||||
return window.API.call(
|
||||
`inventory/template/${params.inventoryTemplateId}`,
|
||||
{
|
||||
method: 'GET',
|
||||
params
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`inventory/template/${params.inventoryTemplateId}`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
// #region | App: Invite Messages
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
|
||||
const inviteMessagesReq = {
|
||||
refreshInviteMessageTableData(messageType) {
|
||||
return window.API.call(
|
||||
`message/${window.API.currentUser.id}/${messageType}`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`message/${getCurrentUserId()}/${messageType}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
messageType
|
||||
};
|
||||
window.API.$emit(`INVITE:${messageType.toUpperCase()}`, args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
editInviteMessage(params, messageType, slot) {
|
||||
return window.API.call(
|
||||
`message/${window.API.currentUser.id}/${messageType}/${slot}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params
|
||||
}
|
||||
).then((json) => {
|
||||
return request(`message/${getCurrentUserId()}/${messageType}/${slot}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params,
|
||||
@@ -36,6 +34,4 @@ const inviteMessagesReq = {
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
export default inviteMessagesReq;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
|
||||
const miscReq = {
|
||||
getBundles(fileId) {
|
||||
return window.API.call(`file/${fileId}`, {
|
||||
return request(`file/${fileId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -11,7 +18,7 @@ const miscReq = {
|
||||
},
|
||||
|
||||
saveNote(params) {
|
||||
return window.API.call('userNotes', {
|
||||
return request('userNotes', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -19,7 +26,6 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('NOTE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -34,7 +40,7 @@ const miscReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
reportUser(params) {
|
||||
return window.API.call(`feedback/${params.userId}/user`, {
|
||||
return request(`feedback/${params.userId}/user`, {
|
||||
method: 'POST',
|
||||
params: {
|
||||
contentType: params.contentType,
|
||||
@@ -46,7 +52,6 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('FEEDBACK:REPORT:USER', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -59,7 +64,7 @@ const miscReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
getFileAnalysis(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`analysis/${params.fileId}/${params.version}/${params.variant}`,
|
||||
{
|
||||
method: 'GET'
|
||||
@@ -69,19 +74,17 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('FILE:ANALYSIS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getVRChatCredits() {
|
||||
return window.API.call(`user/${window.API.currentUser.id}/balance`, {
|
||||
return request(`user/${getCurrentUserId()}/balance`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
// window.API.$emit('VRCCREDITS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -94,7 +97,7 @@ const miscReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
closeInstance(params) {
|
||||
return window.API.call(`instances/${params.location}`, {
|
||||
return request(`instances/${params.location}`, {
|
||||
method: 'DELETE',
|
||||
params: {
|
||||
hardClose: params.hardClose ?? false
|
||||
@@ -104,7 +107,6 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('INSTANCE:CLOSE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -116,8 +118,8 @@ const miscReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
deleteWorldPersistData(params) {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/${params.worldId}/persist`,
|
||||
return request(
|
||||
`users/${getCurrentUserId()}/${params.worldId}/persist`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
@@ -126,7 +128,6 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:PERSIST:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -138,8 +139,8 @@ const miscReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
hasWorldPersistData(params) {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/${params.worldId}/persist/exists`,
|
||||
return request(
|
||||
`users/${getCurrentUserId()}/${params.worldId}/persist/exists`,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
@@ -148,47 +149,41 @@ const miscReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:PERSIST:HAS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
updateBadge(params) {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/badges/${params.badgeId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
params: {
|
||||
userId: window.API.currentUser.id,
|
||||
badgeId: params.badgeId,
|
||||
hidden: params.hidden,
|
||||
showcased: params.showcased
|
||||
}
|
||||
return request(`users/${getCurrentUserId()}/badges/${params.badgeId}`, {
|
||||
method: 'PUT',
|
||||
params: {
|
||||
userId: getCurrentUserId(),
|
||||
badgeId: params.badgeId,
|
||||
hidden: params.hidden,
|
||||
showcased: params.showcased
|
||||
}
|
||||
).then((json) => {
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('BADGE:UPDATE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getVisits() {
|
||||
return window.API.call('visits', {
|
||||
return request('visits', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
// window.API.$emit('VISITS', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
deleteFile(fileId) {
|
||||
return window.API.call(`file/${fileId}`, {
|
||||
return request(`file/${fileId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -207,7 +202,7 @@ const miscReq = {
|
||||
// * @returns {Promise<{json: any, params}>}
|
||||
// */
|
||||
// sendBoop(params) {
|
||||
// return window.API.call(`users/${params.userId}/boop`, {
|
||||
// return request(`users/${params.userId}/boop`, {
|
||||
// method: 'POST',
|
||||
// params
|
||||
// }).then((json) => {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// #region | API: Notification
|
||||
import { request } from '../service/request';
|
||||
import { useGroupStore, useNotificationStore } from '../stores';
|
||||
|
||||
function getGalleryStore() {
|
||||
return useGroupStore();
|
||||
}
|
||||
|
||||
const notificationReq = {
|
||||
/** @typedef {{
|
||||
@@ -17,7 +22,7 @@ const notificationReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getNotifications(params) {
|
||||
return window.API.call('auth/user/notifications', {
|
||||
return request('auth/user/notifications', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -25,13 +30,13 @@ const notificationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:LIST', args);
|
||||
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getHiddenFriendRequests(params) {
|
||||
return window.API.call('auth/user/notifications', {
|
||||
return request('auth/user/notifications', {
|
||||
method: 'GET',
|
||||
params: {
|
||||
type: 'friendRequest',
|
||||
@@ -43,13 +48,12 @@ const notificationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:LIST:HIDDEN', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getNotificationsV2(params) {
|
||||
return window.API.call('notifications', {
|
||||
return request('notifications', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -57,7 +61,6 @@ const notificationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:V2:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -80,7 +83,7 @@ const notificationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
sendInvite(params, receiverUserId) {
|
||||
return window.API.call(`invite/${receiverUserId}`, {
|
||||
return request(`invite/${receiverUserId}`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -89,28 +92,26 @@ const notificationReq = {
|
||||
params,
|
||||
receiverUserId
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:INVITE:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
sendInvitePhoto(params, receiverUserId) {
|
||||
return window.API.call(`invite/${receiverUserId}/photo`, {
|
||||
return request(`invite/${receiverUserId}/photo`, {
|
||||
uploadImageLegacy: true,
|
||||
postData: JSON.stringify(params),
|
||||
imageData: window.$app.uploadImage
|
||||
imageData: getGalleryStore().uploadImage
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params,
|
||||
receiverUserId
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:INVITE:PHOTO:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
sendRequestInvite(params, receiverUserId) {
|
||||
return window.API.call(`requestInvite/${receiverUserId}`, {
|
||||
return request(`requestInvite/${receiverUserId}`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -119,29 +120,27 @@ const notificationReq = {
|
||||
params,
|
||||
receiverUserId
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:REQUESTINVITE:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
sendRequestInvitePhoto(params, receiverUserId) {
|
||||
return window.API.call(`requestInvite/${receiverUserId}/photo`, {
|
||||
return request(`requestInvite/${receiverUserId}/photo`, {
|
||||
uploadImageLegacy: true,
|
||||
postData: JSON.stringify(params),
|
||||
imageData: window.$app.uploadImage
|
||||
imageData: getGalleryStore().uploadImage
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params,
|
||||
receiverUserId
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:REQUESTINVITE:PHOTO:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
sendInviteResponse(params, inviteId) {
|
||||
return window.API.call(`invite/${inviteId}/response`, {
|
||||
return request(`invite/${inviteId}/response`, {
|
||||
method: 'POST',
|
||||
params,
|
||||
inviteId
|
||||
@@ -151,16 +150,15 @@ const notificationReq = {
|
||||
params,
|
||||
inviteId
|
||||
};
|
||||
window.API.$emit('INVITE:RESPONSE:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
sendInviteResponsePhoto(params, inviteId) {
|
||||
return window.API.call(`invite/${inviteId}/response/photo`, {
|
||||
return request(`invite/${inviteId}/response/photo`, {
|
||||
uploadImageLegacy: true,
|
||||
postData: JSON.stringify(params),
|
||||
imageData: window.$app.uploadImage,
|
||||
imageData: getGalleryStore().uploadImage,
|
||||
inviteId
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -168,7 +166,6 @@ const notificationReq = {
|
||||
params,
|
||||
inviteId
|
||||
};
|
||||
window.API.$emit('INVITE:RESPONSE:PHOTO:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -178,7 +175,7 @@ const notificationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
acceptFriendRequestNotification(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`auth/user/notifications/${params.notificationId}/accept`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -189,13 +186,13 @@ const notificationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:ACCEPT', args);
|
||||
useNotificationStore().handleNotificationAccept(args);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
// if friend request could not be found, delete it
|
||||
if (err && err.message && err.message.includes('404')) {
|
||||
window.API.$emit('NOTIFICATION:HIDE', { params });
|
||||
useNotificationStore().handleNotificationHide({ params });
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -205,7 +202,7 @@ const notificationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
hideNotification(params) {
|
||||
return window.API.call(
|
||||
return request(
|
||||
`auth/user/notifications/${params.notificationId}/hide`,
|
||||
{
|
||||
method: 'PUT'
|
||||
@@ -215,13 +212,11 @@ const notificationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:HIDE', args);
|
||||
useNotificationStore().handleNotificationHide(args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
// ------------------- need to test -------------------
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* notificationId: string,
|
||||
@@ -231,32 +226,14 @@ const notificationReq = {
|
||||
* @return { Promise<{json: any, params}> }
|
||||
*/
|
||||
sendNotificationResponse(params) {
|
||||
return window.API.call(
|
||||
`notifications/${params.notificationId}/respond`,
|
||||
{
|
||||
method: 'POST',
|
||||
params
|
||||
}
|
||||
)
|
||||
.then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('NOTIFICATION:RESPONSE', args);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
// TODO: need to test
|
||||
// something went wrong, lets assume it's already expired
|
||||
window.API.$emit('NOTIFICATION:HIDE', { params });
|
||||
notificationReq.hideNotificationV2(params.notificationId);
|
||||
throw err;
|
||||
});
|
||||
return request(`notifications/${params.notificationId}/respond`, {
|
||||
method: 'POST',
|
||||
params
|
||||
});
|
||||
},
|
||||
// use in sendNotificationResponse
|
||||
|
||||
hideNotificationV2(notificationId) {
|
||||
return window.API.call(`notifications/${notificationId}`, {
|
||||
return request(`notifications/${notificationId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
@@ -265,16 +242,12 @@ const notificationReq = {
|
||||
notificationId
|
||||
}
|
||||
};
|
||||
// useless
|
||||
// window.API.$emit('NOTIFICATION:V2:HIDE', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------ look like no place use these requests ------------------
|
||||
|
||||
// sendInviteGalleryPhoto(params, receiverUserId) {
|
||||
// return window.API.call(`invite/${receiverUserId}/photo`, {
|
||||
// return request(`invite/${receiverUserId}/photo`, {
|
||||
// method: 'POST',
|
||||
// params
|
||||
// }).then((json) => {
|
||||
@@ -283,13 +256,13 @@ const notificationReq = {
|
||||
// params,
|
||||
// receiverUserId
|
||||
// };
|
||||
// window.API.$emit('NOTIFICATION:INVITE:GALLERYPHOTO:SEND', args);
|
||||
// API.$emit('NOTIFICATION:INVITE:GALLERYPHOTO:SEND', args);
|
||||
// return args;
|
||||
// });
|
||||
// },
|
||||
|
||||
// API.clearNotifications = function () {
|
||||
// return this.call('auth/user/notifications/clear', {
|
||||
// return request('auth/user/notifications/clear', {
|
||||
// method: 'PUT'
|
||||
// }).then((json) => {
|
||||
// var args = {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// #region | API: PlayerModeration
|
||||
import { request } from '../service/request';
|
||||
|
||||
const playerModerationReq = {
|
||||
getPlayerModerations() {
|
||||
return window.API.call('auth/user/playermoderations', {
|
||||
return request('auth/user/playermoderations', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json
|
||||
};
|
||||
window.API.$emit('PLAYER-MODERATION:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -19,7 +18,7 @@ const playerModerationReq = {
|
||||
*/
|
||||
// old-way: POST auth/user/blocks {blocked:userId}
|
||||
sendPlayerModeration(params) {
|
||||
return window.API.call('auth/user/playermoderations', {
|
||||
return request('auth/user/playermoderations', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -27,7 +26,6 @@ const playerModerationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('PLAYER-MODERATION:SEND', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -38,7 +36,7 @@ const playerModerationReq = {
|
||||
*/
|
||||
// old-way: PUT auth/user/unblocks {blocked:userId}
|
||||
deletePlayerModeration(params) {
|
||||
return window.API.call('auth/user/unplayermoderate', {
|
||||
return request('auth/user/unplayermoderate', {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -46,11 +44,9 @@ const playerModerationReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('PLAYER-MODERATION:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
export default playerModerationReq;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { request } from '../service/request';
|
||||
|
||||
const propReq = {
|
||||
/**
|
||||
* @param {{ propId: string }} params
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getProp(params) {
|
||||
return window.API.call(`props/${params.propId}`, {
|
||||
return request(`props/${params.propId}`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
// #region | API: User
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
|
||||
const userReq = {
|
||||
/**
|
||||
* Fetch user from API.
|
||||
* @param {{ userId: string }} params identifier of registered user
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
* identifier of registered user
|
||||
* @type {import('../types/user').getUser}
|
||||
*/
|
||||
getUser(params) {
|
||||
return window.API.call(`users/${params.userId}`, {
|
||||
const userStore = useUserStore();
|
||||
return request(`users/${params.userId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
if (!json) {
|
||||
@@ -19,7 +25,7 @@ const userReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('USER', args);
|
||||
args.ref = userStore.applyUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -30,10 +36,17 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getCachedUser(params) {
|
||||
const userStore = useUserStore();
|
||||
return new Promise((resolve, reject) => {
|
||||
const ref = window.API.cachedUsers.get(params.userId);
|
||||
const ref = userStore.cachedUsers.get(params.userId);
|
||||
if (typeof ref === 'undefined') {
|
||||
userReq.getUser(params).catch(reject).then(resolve);
|
||||
userReq
|
||||
.getUser(params)
|
||||
.catch(reject)
|
||||
.then((args) => {
|
||||
args.ref = userStore.applyUser(args.json);
|
||||
resolve(args);
|
||||
});
|
||||
} else {
|
||||
resolve({
|
||||
cache: true,
|
||||
@@ -59,7 +72,7 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getUsers(params) {
|
||||
return window.API.call('users', {
|
||||
return request('users', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -67,7 +80,6 @@ const userReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('USER:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -77,7 +89,8 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
addUserTags(params) {
|
||||
return window.API.call(`users/${window.API.currentUser.id}/addTags`, {
|
||||
const userStore = useUserStore();
|
||||
return request(`users/${getCurrentUserId()}/addTags`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -85,7 +98,7 @@ const userReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('USER:CURRENT:SAVE', args);
|
||||
userStore.applyCurrentUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -95,18 +108,16 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
removeUserTags(params) {
|
||||
return window.API.call(
|
||||
`users/${window.API.currentUser.id}/removeTags`,
|
||||
{
|
||||
method: 'POST',
|
||||
params
|
||||
}
|
||||
).then((json) => {
|
||||
const userStore = useUserStore();
|
||||
return request(`users/${getCurrentUserId()}/removeTags`, {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('USER:CURRENT:SAVE', args);
|
||||
userStore.applyCurrentUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -116,7 +127,7 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getUserFeedback(params) {
|
||||
return window.API.call(`users/${params.userId}/feedback`, {
|
||||
return request(`users/${params.userId}/feedback`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
n: 100
|
||||
@@ -126,7 +137,6 @@ const userReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('USER:FEEDBACK', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -144,15 +154,16 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
saveCurrentUser(params) {
|
||||
return window.API.call(`users/${window.API.currentUser.id}`, {
|
||||
const userStore = useUserStore();
|
||||
return request(`users/${getCurrentUserId()}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('USER:CURRENT:SAVE', args);
|
||||
userStore.applyCurrentUser(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -162,7 +173,7 @@ const userReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getUserNotes(params) {
|
||||
return window.API.call(`userNotes`, {
|
||||
return request(`userNotes`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -170,11 +181,9 @@ const userReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
// window.API.$emit('USER:NOTES', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
export default userReq;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// #region | App: VRCPlus Icons
|
||||
import { request } from '../service/request';
|
||||
|
||||
const VRCPlusIconsReq = {
|
||||
getFileList(params) {
|
||||
return window.API.call('files', {
|
||||
return request('files', {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -10,7 +10,18 @@ const VRCPlusIconsReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('FILES:LIST', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
deleteFile(fileId) {
|
||||
return request(`file/${fileId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
fileId
|
||||
};
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -19,7 +30,7 @@ const VRCPlusIconsReq = {
|
||||
const params = {
|
||||
tag: 'icon'
|
||||
};
|
||||
return window.API.call('file/image', {
|
||||
return request('file/image', {
|
||||
uploadImage: true,
|
||||
matchingDimensions: true,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -29,15 +40,12 @@ const VRCPlusIconsReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('VRCPLUSICON:ADD', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
// unused
|
||||
// images.pug line 63
|
||||
// deleteFileVersion(params) {
|
||||
// return window.API.call(`file/${params.fileId}/${params.version}`, {
|
||||
// return request(`file/${params.fileId}/${params.version}`, {
|
||||
// method: 'DELETE'
|
||||
// }).then((json) => {
|
||||
// const args = {
|
||||
@@ -49,6 +57,4 @@ const VRCPlusIconsReq = {
|
||||
// }
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
export default VRCPlusIconsReq;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { request } from '../service/request';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
function getCurrentUserId() {
|
||||
return useUserStore().currentUser.id;
|
||||
}
|
||||
const vrcPlusImageReq = {
|
||||
uploadGalleryImage(imageData) {
|
||||
const params = {
|
||||
tag: 'gallery'
|
||||
};
|
||||
return window.API.call('file/image', {
|
||||
return request('file/image', {
|
||||
uploadImage: true,
|
||||
matchingDimensions: false,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -13,13 +19,12 @@ const vrcPlusImageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('GALLERYIMAGE:ADD', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
uploadSticker(imageData, params) {
|
||||
return window.API.call('file/image', {
|
||||
return request('file/image', {
|
||||
uploadImage: true,
|
||||
matchingDimensions: true,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -29,13 +34,12 @@ const vrcPlusImageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('STICKER:ADD', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getPrints(params) {
|
||||
return window.API.call(`prints/user/${window.API.currentUser.id}`, {
|
||||
return request(`prints/user/${getCurrentUserId()}`, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -48,20 +52,19 @@ const vrcPlusImageReq = {
|
||||
},
|
||||
|
||||
deletePrint(printId) {
|
||||
return window.API.call(`prints/${printId}`, {
|
||||
return request(`prints/${printId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
printId
|
||||
};
|
||||
// window.API.$emit('PRINT:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
uploadPrint(imageData, cropWhiteBorder, params) {
|
||||
return window.API.call('prints', {
|
||||
return request('prints', {
|
||||
uploadImagePrint: true,
|
||||
cropWhiteBorder,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -71,26 +74,24 @@ const vrcPlusImageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('PRINT:ADD', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
getPrint(params) {
|
||||
return window.API.call(`prints/${params.printId}`, {
|
||||
return request(`prints/${params.printId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('PRINT', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
|
||||
uploadEmoji(imageData, params) {
|
||||
return window.API.call('file/image', {
|
||||
return request('file/image', {
|
||||
uploadImage: true,
|
||||
matchingDimensions: true,
|
||||
postData: JSON.stringify(params),
|
||||
@@ -100,15 +101,12 @@ const vrcPlusImageReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('EMOJI:ADD', args);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
// ----------- no place uses this function ------------
|
||||
|
||||
// editPrint(params) {
|
||||
// return window.API.call(`prints/${params.printId}`, {
|
||||
// return request(`prints/${params.printId}`, {
|
||||
// method: 'POST',
|
||||
// params
|
||||
// }).then((json) => {
|
||||
@@ -116,7 +114,7 @@ const vrcPlusImageReq = {
|
||||
// json,
|
||||
// params
|
||||
// };
|
||||
// window.API.$emit('PRINT:EDIT', args);
|
||||
// API.$emit('PRINT:EDIT', args);
|
||||
// return args;
|
||||
// });
|
||||
// },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// #region | API: World
|
||||
import { request } from '../service/request';
|
||||
import { useWorldStore } from '../stores';
|
||||
|
||||
const worldReq = {
|
||||
/**
|
||||
@@ -6,14 +7,15 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getWorld(params) {
|
||||
return window.API.call(`worlds/${params.worldId}`, {
|
||||
const worldStore = useWorldStore();
|
||||
return request(`worlds/${params.worldId}`, {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD', args);
|
||||
args.ref = worldStore.applyWorld(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -23,10 +25,17 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
getCachedWorld(params) {
|
||||
const worldStore = useWorldStore();
|
||||
return new Promise((resolve, reject) => {
|
||||
const ref = window.API.cachedWorlds.get(params.worldId);
|
||||
const ref = worldStore.cachedWorlds.get(params.worldId);
|
||||
if (typeof ref === 'undefined') {
|
||||
worldReq.getWorld(params).catch(reject).then(resolve);
|
||||
worldReq
|
||||
.getWorld(params)
|
||||
.catch(reject)
|
||||
.then((args) => {
|
||||
args.ref = worldStore.applyWorld(args.json);
|
||||
resolve(args);
|
||||
});
|
||||
} else {
|
||||
resolve({
|
||||
cache: true,
|
||||
@@ -57,11 +66,12 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params, option}>}
|
||||
*/
|
||||
getWorlds(params, option) {
|
||||
const worldStore = useWorldStore();
|
||||
let endpoint = 'worlds';
|
||||
if (typeof option !== 'undefined') {
|
||||
endpoint = `worlds/${option}`;
|
||||
}
|
||||
return window.API.call(endpoint, {
|
||||
return request(endpoint, {
|
||||
method: 'GET',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -70,7 +80,9 @@ const worldReq = {
|
||||
params,
|
||||
option
|
||||
};
|
||||
window.API.$emit('WORLD:LIST', args);
|
||||
for (const json of args.json) {
|
||||
worldStore.applyWorld(json);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -79,14 +91,13 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
deleteWorld(params) {
|
||||
return window.API.call(`worlds/${params.worldId}`, {
|
||||
return request(`worlds/${params.worldId}`, {
|
||||
method: 'DELETE'
|
||||
}).then((json) => {
|
||||
const args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:DELETE', args);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -96,7 +107,8 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
saveWorld(params) {
|
||||
return window.API.call(`worlds/${params.id}`, {
|
||||
const worldStore = useWorldStore();
|
||||
return request(`worlds/${params.id}`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -104,7 +116,7 @@ const worldReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:SAVE', args);
|
||||
args.ref = worldStore.applyWorld(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -114,7 +126,8 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
publishWorld(params) {
|
||||
return window.API.call(`worlds/${params.worldId}/publish`, {
|
||||
const worldStore = useWorldStore();
|
||||
return request(`worlds/${params.worldId}/publish`, {
|
||||
method: 'PUT',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -122,7 +135,7 @@ const worldReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:SAVE', args);
|
||||
args.ref = worldStore.applyWorld(json);
|
||||
return args;
|
||||
});
|
||||
},
|
||||
@@ -132,7 +145,8 @@ const worldReq = {
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
unpublishWorld(params) {
|
||||
return window.API.call(`worlds/${params.worldId}/publish`, {
|
||||
const worldStore = useWorldStore();
|
||||
return request(`worlds/${params.worldId}/publish`, {
|
||||
method: 'DELETE',
|
||||
params
|
||||
}).then((json) => {
|
||||
@@ -140,12 +154,10 @@ const worldReq = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
window.API.$emit('WORLD:SAVE', args);
|
||||
args.ref = worldStore.applyWorld(json);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
export default worldReq;
|
||||
|
||||
14159
src/app.js
14159
src/app.js
File diff suppressed because it is too large
Load Diff
94
src/app.pug
94
src/app.pug
@@ -1,63 +1,71 @@
|
||||
doctype html
|
||||
#x-app.x-app(@dragenter.prevent @dragover.prevent @drop.prevent)
|
||||
LoginPage(v-if="!API.isLoggedIn" v-bind="loginPageBind" v-on="loginPageEvent")
|
||||
//- ### Login ###
|
||||
Login(v-if='!watchState.isLoggedIn')
|
||||
|
||||
VRCXUpdateDialog(v-bind="vrcxUpdateDialogBind" v-on="vrcxUpdateDialogEvent")
|
||||
VRCXUpdateDialog
|
||||
|
||||
//- menu
|
||||
.x-menu-container
|
||||
//- download progress, update pending
|
||||
.pending-update(v-if='updateInProgress' @click='showVRCXUpdateDialog')
|
||||
el-progress(
|
||||
type='circle'
|
||||
width='50'
|
||||
stroke-width='3'
|
||||
:percentage='updateProgress'
|
||||
:format='updateProgressText')
|
||||
.pending-update(v-else-if='pendingVRCXUpdate || pendingVRCXInstall')
|
||||
el-button(
|
||||
type='default'
|
||||
@click='showVRCXUpdateDialog'
|
||||
size='mini'
|
||||
icon='el-icon-download'
|
||||
circle
|
||||
style='font-size: 14px; height: 50px; width: 50px')
|
||||
template(v-if='watchState.isLoggedIn')
|
||||
//- ### Menu ###
|
||||
NavMenu
|
||||
|
||||
NavMenu(ref='menu' @select='selectMenu' :menu-active-index='menuActiveIndex')
|
||||
//- ### Sidebar ###
|
||||
Sidebar
|
||||
|
||||
//- ### Tabs ###
|
||||
template(v-if='API.isLoggedIn')
|
||||
FeedTab(v-bind='feedTabBind' v-on='feedTabEvent')
|
||||
//- ### Tabs ###
|
||||
Feed
|
||||
|
||||
GameLogTab(v-bind='gameLogTabBind' v-on='gameLogTabEvent')
|
||||
GameLog
|
||||
|
||||
PlayerListTab(v-bind='playerListTabBind' v-on='playerListTabEvent')
|
||||
PlayerList
|
||||
|
||||
SearchTab(v-bind='searchTabBind' v-on='searchTabEvent')
|
||||
Search
|
||||
|
||||
FavoritesTab(v-bind='favoritesTabBind' v-on='favoritesTabEvent')
|
||||
Favorites
|
||||
|
||||
FriendLogTab(v-bind='friendLogTabBind')
|
||||
FriendLog
|
||||
|
||||
ModerationTab(v-bind='moderationTabBind')
|
||||
Moderation
|
||||
|
||||
NotificationTab(v-bind='notificationTabBind' v-on='notificationTabEvent')
|
||||
Notification
|
||||
|
||||
ProfileTab(v-bind='profileTabBind' v-on='profileTabEvent')
|
||||
FriendList
|
||||
|
||||
FriendListTab(v-bind='friendsListTabBind' v-on='friendsListTabEvent')
|
||||
Charts
|
||||
|
||||
KeepAlive
|
||||
ChartsTab(v-if='menuActiveIndex === "charts"' v-bind='chartsTabBind' v-on='chartsTabEvent')
|
||||
Profile
|
||||
|
||||
//- settings
|
||||
include ./mixins/tabs/settings.pug
|
||||
+settingsTab
|
||||
|
||||
SideBar(v-bind='sideBarTabBind' v-on='sideBarTabEvent')
|
||||
Settings
|
||||
|
||||
//- ## Dialogs ## -\\
|
||||
include ./mixins/dialogs/dialogs.pug
|
||||
+dialogs
|
||||
UserDialog
|
||||
|
||||
//- el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="templateDialog" :visible.sync="templateDialog.visible" :title="$t('dialog.template_dialog.header')" width="450px")
|
||||
WorldDialog
|
||||
|
||||
AvatarDialog
|
||||
|
||||
GroupDialog
|
||||
|
||||
GalleryDialog
|
||||
|
||||
FullscreenImageDialog
|
||||
|
||||
PreviousInstancesInfoDialog
|
||||
|
||||
LaunchDialog
|
||||
|
||||
LaunchOptionsDialog
|
||||
|
||||
FriendImportDialog
|
||||
|
||||
WorldImportDialog
|
||||
|
||||
AvatarImportDialog
|
||||
|
||||
ChooseFavoriteGroupDialog
|
||||
|
||||
EditInviteMessageDialog
|
||||
|
||||
VRChatConfigDialog
|
||||
|
||||
PrimaryPasswordDialog
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
//
|
||||
|
||||
@use "./assets/scss/flags.scss";
|
||||
@use "./assets/scss/animated-emoji.scss";
|
||||
|
||||
@import '~animate.css/animate.min.css';
|
||||
@import '~noty/lib/noty.css';
|
||||
@import '~element-ui/lib/theme-chalk/index.css';
|
||||
@@ -299,6 +302,7 @@ hr.x-vertical-divider {
|
||||
flex-direction: column;
|
||||
background: #f8f8f8;
|
||||
padding: 5px;
|
||||
order: 99;
|
||||
}
|
||||
|
||||
.el-popper.x-quick-search {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* VRCX Dark-Vanilla theme by MintLily
|
||||
* https://github.com/MintLily/Dark-Vanilla
|
||||
*/
|
||||
@import 'theme.dark';
|
||||
@use 'theme.dark';
|
||||
:root {
|
||||
--ThemeName: 'Dark Vanilla';
|
||||
--ThemeVersion: 'v1.7';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* VRCX Material-You-like theme by Kamiya
|
||||
* https://github.com/kamiya10/VRCX-theme
|
||||
*/
|
||||
@import 'theme.dark';
|
||||
@use 'theme.dark';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;600&family=Noto+Sans+TC:wght@300;400;500&family=Noto+Sans+SC:wght@300;400;500&family=Noto+Sans+JP:wght@300;400;500&family=Roboto&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
|
||||
|
||||
@@ -653,6 +653,8 @@ input[type='number'],
|
||||
border-left: 1px solid rgb(var(--md-sys-color-surface-variant));
|
||||
*/
|
||||
padding: 8px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/* ---------- Switch ---------- */
|
||||
|
||||
@@ -1083,8 +1085,8 @@ input[type='number'],
|
||||
border-radius: 8px;
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
margin-top: 8px !important;
|
||||
margin-right: 8px !important;
|
||||
// margin-top: 8px !important;
|
||||
// margin-right: 8px !important;
|
||||
color: rgb(var(--md-sys-color-on-surface-variant));
|
||||
font-family: var(--md-sys-typescale-label-large-font);
|
||||
line-height: 28px;
|
||||
@@ -1380,7 +1382,7 @@ img.x-link.el-popover__reference {
|
||||
*:not(.x-user-dialog, .x-world-dialog, .x-avatar-dialog, .x-group-dialog)
|
||||
> .el-dialog:not([aria-label*='Notification Position']):not(
|
||||
[aria-label*='Launch']
|
||||
):not([aria-label*='VRCX Updater']) {
|
||||
):not([aria-label*='VRCX Updater']):not([aria-label*='Social Status']) {
|
||||
max-width: 1125px !important;
|
||||
width: 1125px !important;
|
||||
}
|
||||
@@ -2008,6 +2010,7 @@ i.x-user-status {
|
||||
.el-collapse-item .el-tag--mini {
|
||||
background-color: transparent;
|
||||
border: transparent;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.simple-switch {
|
||||
justify-content: space-between;
|
||||
@@ -2071,4 +2074,12 @@ i.x-user-status {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.el-dialog[aria-label='Launch'] .el-form .el-form-item__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.el-dialog[aria-label='Launch'] .el-form > .el-form-item:nth-child(2) .el-form-item__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* VRCX Pink theme by Kamiya
|
||||
* https://github.com/kamiya10/VRCX-theme
|
||||
*/
|
||||
@import 'theme.dark';
|
||||
@use 'theme.dark';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
|
||||
:root {
|
||||
--theme: #dfa2a2;
|
||||
@@ -70,7 +70,7 @@ textarea {
|
||||
.el-textarea .el-input__count,
|
||||
.el-textarea__inner,
|
||||
.x-menu-container {
|
||||
background-color: var(--lighter-bg);
|
||||
background-color: var(--lighter-bg) !important;
|
||||
}
|
||||
.el-color-picker__panel {
|
||||
border-color: var(--lighter-bg);
|
||||
|
||||
76
src/bootstrap.js
vendored
Normal file
76
src/bootstrap.js
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import '@fontsource/noto-sans-kr';
|
||||
import '@fontsource/noto-sans-jp';
|
||||
import '@fontsource/noto-sans-sc';
|
||||
import '@fontsource/noto-sans-tc';
|
||||
import '@infolektuell/noto-color-emoji';
|
||||
|
||||
import Vue from 'vue';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { DataTables } from 'vue-data-tables';
|
||||
import VueLazyload from 'vue-lazyload';
|
||||
import configRepository from './service/config';
|
||||
import vrcxJsonStorage from './service/jsonStorage';
|
||||
import {
|
||||
changeAppDarkStyle,
|
||||
changeAppThemeStyle,
|
||||
refreshCustomCss,
|
||||
refreshCustomScript,
|
||||
systemIsDarkMode
|
||||
} from './shared/utils';
|
||||
import { i18n } from './plugin';
|
||||
|
||||
configRepository.init();
|
||||
i18n.locale = await configRepository.getString('VRCX_appLanguage', 'en');
|
||||
|
||||
AppApi.SetUserAgent();
|
||||
|
||||
const initThemeMode = await configRepository.getString(
|
||||
'VRCX_ThemeMode',
|
||||
'system'
|
||||
);
|
||||
|
||||
let isDarkMode;
|
||||
|
||||
if (initThemeMode === 'light') {
|
||||
isDarkMode = false;
|
||||
} else if (initThemeMode === 'system') {
|
||||
isDarkMode = systemIsDarkMode();
|
||||
} else {
|
||||
isDarkMode = true;
|
||||
}
|
||||
changeAppDarkStyle(isDarkMode);
|
||||
changeAppThemeStyle(initThemeMode);
|
||||
|
||||
refreshCustomCss();
|
||||
refreshCustomScript();
|
||||
|
||||
Vue.use(PiniaVuePlugin);
|
||||
Vue.use(DataTables);
|
||||
|
||||
Vue.use(VueLazyload, {
|
||||
preLoad: 1,
|
||||
observer: true,
|
||||
observerOptions: {
|
||||
rootMargin: '0px',
|
||||
threshold: 0
|
||||
},
|
||||
attempt: 3
|
||||
});
|
||||
|
||||
new vrcxJsonStorage(VRCXStorage);
|
||||
|
||||
// some workaround for failing to get voice list first run
|
||||
speechSynthesis.getVoices();
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Vue.config.errorHandler = function (err, vm, info) {
|
||||
console.error('Vue Error:', err);
|
||||
console.error('Component:', vm);
|
||||
console.error('Error Info:', info);
|
||||
};
|
||||
Vue.config.warnHandler = function (msg, vm, trace) {
|
||||
console.warn('Vue Warning:', msg);
|
||||
console.warn('Component:', vm);
|
||||
console.warn('Trace:', trace);
|
||||
};
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { baseClass, $app, API, $t, $utils } from '../baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.getConfig = function () {
|
||||
return this.call('config', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json
|
||||
};
|
||||
this.$emit('CONFIG', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('CONFIG', function (args) {
|
||||
args.ref = this.applyConfig(args.json);
|
||||
});
|
||||
|
||||
API.$on('CONFIG', function (args) {
|
||||
if (typeof args.ref?.whiteListedAssetUrls !== 'object') {
|
||||
console.error('Invalid config whiteListedAssetUrls');
|
||||
}
|
||||
AppApi.PopulateImageHosts(
|
||||
JSON.stringify(args.ref.whiteListedAssetUrls)
|
||||
);
|
||||
});
|
||||
|
||||
API.applyConfig = function (json) {
|
||||
var ref = {
|
||||
...json
|
||||
};
|
||||
this.cachedConfig = ref;
|
||||
return ref;
|
||||
};
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../service/config.js';
|
||||
import database from '../service/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app) {
|
||||
super(_app);
|
||||
}
|
||||
|
||||
eventHandlers = new Map();
|
||||
|
||||
$emit = function (name, ...args) {
|
||||
if ($app.debug) {
|
||||
console.log(name, ...args);
|
||||
}
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (var handler of handlers) {
|
||||
handler.apply(this, args);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
$on = function (name, handler) {
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
handlers = [];
|
||||
this.eventHandlers.set(name, handlers);
|
||||
}
|
||||
handlers.push(handler);
|
||||
};
|
||||
|
||||
$off = function (name, handler) {
|
||||
var handlers = this.eventHandlers.get(name);
|
||||
if (typeof handlers === 'undefined') {
|
||||
return;
|
||||
}
|
||||
var { length } = handlers;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (handlers[i] === handler) {
|
||||
if (length > 1) {
|
||||
handlers.splice(i, 1);
|
||||
} else {
|
||||
this.eventHandlers.delete(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
import Noty from 'noty';
|
||||
import security from '../service/security.js';
|
||||
import configRepository from '../service/config.js';
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let webApiService = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t, _webApiService) {
|
||||
super(_app, _API, _t);
|
||||
webApiService = _webApiService;
|
||||
}
|
||||
|
||||
async init() {
|
||||
API.isLoggedIn = false;
|
||||
API.attemptingAutoLogin = false;
|
||||
API.autoLoginAttempts = new Set();
|
||||
|
||||
/**
|
||||
* @param {{ username: string, password: string }} params credential to login
|
||||
* @returns {Promise<{origin: boolean, json: any, params}>}
|
||||
*/
|
||||
API.login = function (params) {
|
||||
var { username, password, saveCredentials, cipher } = params;
|
||||
username = encodeURIComponent(username);
|
||||
password = encodeURIComponent(password);
|
||||
var auth = btoa(`${username}:${password}`);
|
||||
if (saveCredentials) {
|
||||
delete params.saveCredentials;
|
||||
if (cipher) {
|
||||
params.password = cipher;
|
||||
delete params.cipher;
|
||||
}
|
||||
$app.saveCredentials = params;
|
||||
}
|
||||
return this.call('auth/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Basic ${auth}`
|
||||
}
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params,
|
||||
origin: true
|
||||
};
|
||||
if (
|
||||
json.requiresTwoFactorAuth &&
|
||||
json.requiresTwoFactorAuth.includes('emailOtp')
|
||||
) {
|
||||
this.$emit('USER:EMAILOTP', args);
|
||||
} else if (json.requiresTwoFactorAuth) {
|
||||
this.$emit('USER:2FA', args);
|
||||
} else {
|
||||
this.$emit('USER:CURRENT', args);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time password
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/otp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('OTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyTOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/totp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('TOTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{ code: string }} params One-time token
|
||||
* @returns {Promise<{json: any, params}>}
|
||||
*/
|
||||
API.verifyEmailOTP = function (params) {
|
||||
return this.call('auth/twofactorauth/emailotp/verify', {
|
||||
method: 'POST',
|
||||
params
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
params
|
||||
};
|
||||
this.$emit('EMAILOTP', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('AUTOLOGIN', function () {
|
||||
if (this.attemptingAutoLogin) {
|
||||
return;
|
||||
}
|
||||
this.attemptingAutoLogin = true;
|
||||
var user =
|
||||
$app.loginForm.savedCredentials[
|
||||
$app.loginForm.lastUserLoggedIn
|
||||
];
|
||||
if (typeof user === 'undefined') {
|
||||
this.attemptingAutoLogin = false;
|
||||
return;
|
||||
}
|
||||
if ($app.enablePrimaryPassword) {
|
||||
console.error(
|
||||
'Primary password is enabled, this disables auto login.'
|
||||
);
|
||||
this.attemptingAutoLogin = false;
|
||||
this.logout();
|
||||
return;
|
||||
}
|
||||
var attemptsInLastHour = Array.from(this.autoLoginAttempts).filter(
|
||||
(timestamp) => timestamp > new Date().getTime() - 3600000
|
||||
).length;
|
||||
if (attemptsInLastHour >= 3) {
|
||||
console.error(
|
||||
'More than 3 auto login attempts within the past hour, logging out instead of attempting auto login.'
|
||||
);
|
||||
this.attemptingAutoLogin = false;
|
||||
this.logout();
|
||||
return;
|
||||
}
|
||||
this.autoLoginAttempts.add(new Date().getTime());
|
||||
$app.relogin(user)
|
||||
.then(() => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'success',
|
||||
text: 'Automatically logged in.'
|
||||
}).show();
|
||||
console.log('Automatically logged in.');
|
||||
})
|
||||
.catch((err) => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: 'Failed to login automatically.'
|
||||
}).show();
|
||||
console.error('Failed to login automatically.', err);
|
||||
})
|
||||
.finally(() => {
|
||||
if (!navigator.onLine) {
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: `You're offline.`
|
||||
}).show();
|
||||
console.error(`You're offline.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
API.$on('USER:CURRENT', function () {
|
||||
this.attemptingAutoLogin = false;
|
||||
});
|
||||
|
||||
API.$on('LOGOUT', function () {
|
||||
this.attemptingAutoLogin = false;
|
||||
this.autoLoginAttempts.clear();
|
||||
});
|
||||
|
||||
API.logout = function () {
|
||||
this.$emit('LOGOUT');
|
||||
// return this.call('logout', {
|
||||
// method: 'PUT'
|
||||
// }).finally(() => {
|
||||
// this.$emit('LOGOUT');
|
||||
// });
|
||||
};
|
||||
}
|
||||
|
||||
_data = {
|
||||
loginForm: {
|
||||
loading: true,
|
||||
username: '',
|
||||
password: '',
|
||||
endpoint: '',
|
||||
websocket: '',
|
||||
saveCredentials: false,
|
||||
savedCredentials: {},
|
||||
lastUserLoggedIn: '',
|
||||
rules: {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async relogin(user) {
|
||||
var { loginParmas } = user;
|
||||
if (user.cookies) {
|
||||
await webApiService.setCookies(user.cookies);
|
||||
}
|
||||
this.loginForm.lastUserLoggedIn = user.user.id; // for resend email 2fa
|
||||
if (loginParmas.endpoint) {
|
||||
API.endpointDomain = loginParmas.endpoint;
|
||||
API.websocketDomain = loginParmas.websocket;
|
||||
} else {
|
||||
API.endpointDomain = API.endpointDomainVrchat;
|
||||
API.websocketDomain = API.websocketDomainVrchat;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loginForm.loading = true;
|
||||
if (this.enablePrimaryPassword) {
|
||||
this.checkPrimaryPassword(loginParmas)
|
||||
.then((pwd) => {
|
||||
return API.getConfig()
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
.then(() => {
|
||||
API.login({
|
||||
username: loginParmas.username,
|
||||
password: pwd,
|
||||
cipher: loginParmas.password,
|
||||
endpoint: loginParmas.endpoint,
|
||||
websocket: loginParmas.websocket
|
||||
})
|
||||
.catch((err2) => {
|
||||
// API.logout();
|
||||
reject(err2);
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((_) => {
|
||||
this.$message({
|
||||
message: 'Incorrect primary password',
|
||||
type: 'error'
|
||||
});
|
||||
reject(_);
|
||||
});
|
||||
} else {
|
||||
API.getConfig()
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
.then(() => {
|
||||
API.login({
|
||||
username: loginParmas.username,
|
||||
password: loginParmas.password,
|
||||
endpoint: loginParmas.endpoint,
|
||||
websocket: loginParmas.websocket
|
||||
})
|
||||
.catch((err2) => {
|
||||
API.logout();
|
||||
reject(err2);
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}).finally(() => (this.loginForm.loading = false));
|
||||
},
|
||||
|
||||
async deleteSavedLogin(userId) {
|
||||
var savedCredentials = JSON.parse(
|
||||
await configRepository.getString('savedCredentials')
|
||||
);
|
||||
delete savedCredentials[userId];
|
||||
// Disable primary password when no account is available.
|
||||
if (Object.keys(savedCredentials).length === 0) {
|
||||
this.enablePrimaryPassword = false;
|
||||
await configRepository.setBool('enablePrimaryPassword', false);
|
||||
}
|
||||
this.loginForm.savedCredentials = savedCredentials;
|
||||
var jsonCredentials = JSON.stringify(savedCredentials);
|
||||
await configRepository.setString(
|
||||
'savedCredentials',
|
||||
jsonCredentials
|
||||
);
|
||||
new Noty({
|
||||
type: 'success',
|
||||
text: 'Account removed.'
|
||||
}).show();
|
||||
},
|
||||
|
||||
async login() {
|
||||
await webApiService.clearCookies();
|
||||
if (!this.loginForm.loading) {
|
||||
this.loginForm.loading = true;
|
||||
if (this.loginForm.endpoint) {
|
||||
API.endpointDomain = this.loginForm.endpoint;
|
||||
API.websocketDomain = this.loginForm.websocket;
|
||||
} else {
|
||||
API.endpointDomain = API.endpointDomainVrchat;
|
||||
API.websocketDomain = API.websocketDomainVrchat;
|
||||
}
|
||||
API.getConfig()
|
||||
.catch((err) => {
|
||||
this.loginForm.loading = false;
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
if (
|
||||
this.loginForm.saveCredentials &&
|
||||
this.enablePrimaryPassword
|
||||
) {
|
||||
$app.$prompt(
|
||||
$t('prompt.primary_password.description'),
|
||||
$t('prompt.primary_password.header'),
|
||||
{
|
||||
inputType: 'password',
|
||||
inputPattern: /[\s\S]{1,32}/
|
||||
}
|
||||
)
|
||||
.then(({ value }) => {
|
||||
let saveCredential =
|
||||
this.loginForm.savedCredentials[
|
||||
Object.keys(
|
||||
this.loginForm.savedCredentials
|
||||
)[0]
|
||||
];
|
||||
security
|
||||
.decrypt(
|
||||
saveCredential.loginParmas.password,
|
||||
value
|
||||
)
|
||||
.then(() => {
|
||||
security
|
||||
.encrypt(
|
||||
this.loginForm.password,
|
||||
value
|
||||
)
|
||||
.then((pwd) => {
|
||||
API.login({
|
||||
username:
|
||||
this.loginForm
|
||||
.username,
|
||||
password:
|
||||
this.loginForm
|
||||
.password,
|
||||
endpoint:
|
||||
this.loginForm
|
||||
.endpoint,
|
||||
websocket:
|
||||
this.loginForm
|
||||
.websocket,
|
||||
saveCredentials:
|
||||
this.loginForm
|
||||
.saveCredentials,
|
||||
cipher: pwd
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.loginForm.loading = false;
|
||||
});
|
||||
return args;
|
||||
}
|
||||
API.login({
|
||||
username: this.loginForm.username,
|
||||
password: this.loginForm.password,
|
||||
endpoint: this.loginForm.endpoint,
|
||||
websocket: this.loginForm.websocket,
|
||||
saveCredentials: this.loginForm.saveCredentials
|
||||
}).finally(() => {
|
||||
this.loginForm.loading = false;
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
logout() {
|
||||
this.$confirm('Continue? Logout', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
API.logout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,402 +0,0 @@
|
||||
import Noty from 'noty';
|
||||
import { baseClass, $app, API, $t } from './baseClass.js';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let webApiService = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t, _webApiService) {
|
||||
super(_app, _API, _t);
|
||||
webApiService = _webApiService;
|
||||
}
|
||||
|
||||
init() {
|
||||
API.cachedConfig = {};
|
||||
API.pendingGetRequests = new Map();
|
||||
API.failedGetRequests = new Map();
|
||||
API.endpointDomainVrchat = 'https://api.vrchat.cloud/api/1';
|
||||
API.websocketDomainVrchat = 'wss://pipeline.vrchat.cloud';
|
||||
API.endpointDomain = 'https://api.vrchat.cloud/api/1';
|
||||
API.websocketDomain = 'wss://pipeline.vrchat.cloud';
|
||||
|
||||
API.call = function (endpoint, options) {
|
||||
var init = {
|
||||
url: `${API.endpointDomain}/${endpoint}`,
|
||||
method: 'GET',
|
||||
...options
|
||||
};
|
||||
var { params } = init;
|
||||
if (init.method === 'GET') {
|
||||
// don't retry recent 404/403
|
||||
if (this.failedGetRequests.has(endpoint)) {
|
||||
var lastRun = this.failedGetRequests.get(endpoint);
|
||||
if (lastRun >= Date.now() - 900000) {
|
||||
// 15mins
|
||||
throw new Error(
|
||||
`${$t('api.error.message.403_404_bailing_request')}, ${endpoint}`
|
||||
);
|
||||
}
|
||||
this.failedGetRequests.delete(endpoint);
|
||||
}
|
||||
// transform body to url
|
||||
if (params === Object(params)) {
|
||||
var url = new URL(init.url);
|
||||
var { searchParams } = url;
|
||||
for (var key in params) {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
init.url = url.toString();
|
||||
}
|
||||
// merge requests
|
||||
var req = this.pendingGetRequests.get(init.url);
|
||||
if (typeof req !== 'undefined') {
|
||||
if (req.time >= Date.now() - 10000) {
|
||||
// 10s
|
||||
return req.req;
|
||||
}
|
||||
this.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) : '{}';
|
||||
}
|
||||
var req = webApiService
|
||||
.execute(init)
|
||||
.catch((err) => {
|
||||
this.$throw(0, err, endpoint);
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
if ($app.debugWebRequests) {
|
||||
console.log(init, response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
response.data = JSON.parse(response.data);
|
||||
if ($app.debugWebRequests) {
|
||||
console.log(init, response.data);
|
||||
}
|
||||
return response;
|
||||
} catch (e) {}
|
||||
if (response.status === 200) {
|
||||
this.$throw(
|
||||
0,
|
||||
$t('api.error.message.invalid_json_response'),
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
if (
|
||||
response.status === 429 &&
|
||||
init.url.endsWith('/instances/groups')
|
||||
) {
|
||||
$app.nextGroupInstanceRefresh = 120; // 1min
|
||||
throw new Error(
|
||||
`${response.status}: rate limited ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (response.status === 504 || response.status === 502) {
|
||||
// ignore expected API errors
|
||||
throw new Error(
|
||||
`${response.status}: ${response.data} ${endpoint}`
|
||||
);
|
||||
}
|
||||
this.$throw(response.status, endpoint);
|
||||
return {};
|
||||
})
|
||||
.then(({ data, status }) => {
|
||||
if (status === 200) {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
var 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: $app.escapeTag(text)
|
||||
}).show();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
if (
|
||||
status === 401 &&
|
||||
data.error.message === '"Missing Credentials"'
|
||||
) {
|
||||
this.$emit('AUTOLOGIN');
|
||||
throw new Error(
|
||||
`401 ${$t('api.error.message.missing_credentials')}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === 401 &&
|
||||
data.error.message === '"Unauthorized"' &&
|
||||
endpoint !== 'auth/user'
|
||||
) {
|
||||
// trigger 2FA dialog
|
||||
if (!$app.twoFactorAuthDialogVisible) {
|
||||
$app.API.getCurrentUser();
|
||||
}
|
||||
throw new Error(`401 ${$t('api.status_code.401')}`);
|
||||
}
|
||||
if (status === 403 && endpoint === 'config') {
|
||||
$app.$alert(
|
||||
$t('api.error.message.vpn_in_use'),
|
||||
`403 ${$t('api.error.message.login_error')}`
|
||||
);
|
||||
this.logout();
|
||||
throw new Error(`403 ${endpoint}`);
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('avatars/')
|
||||
) {
|
||||
$app.$message({
|
||||
message: $t(
|
||||
'message.api_handler.avatar_private_or_deleted'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
$app.avatarDialog.visible = false;
|
||||
throw new Error(
|
||||
`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')
|
||||
) {
|
||||
this.failedGetRequests.set(endpoint, Date.now());
|
||||
}
|
||||
if (
|
||||
init.method === 'GET' &&
|
||||
status === 404 &&
|
||||
endpoint.startsWith('users/') &&
|
||||
endpoint.split('/').length - 1 === 1
|
||||
) {
|
||||
throw new Error(
|
||||
`404: ${data.error.message} ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
status === 404 &&
|
||||
endpoint.startsWith('invite/') &&
|
||||
init.inviteId
|
||||
) {
|
||||
this.expireNotification(init.inviteId);
|
||||
}
|
||||
if (
|
||||
status === 403 &&
|
||||
endpoint.startsWith('invite/myself/to/')
|
||||
) {
|
||||
throw new Error(
|
||||
`403: ${data.error.message} ${endpoint}`
|
||||
);
|
||||
}
|
||||
if (data && data.error === Object(data.error)) {
|
||||
this.$throw(
|
||||
data.error.status_code || status,
|
||||
data.error.message,
|
||||
endpoint
|
||||
);
|
||||
} else if (data && typeof data.error === 'string') {
|
||||
this.$throw(
|
||||
data.status_code || status,
|
||||
data.error,
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
this.$throw(status, data, endpoint);
|
||||
return data;
|
||||
});
|
||||
if (init.method === 'GET') {
|
||||
req.finally(() => {
|
||||
this.pendingGetRequests.delete(init.url);
|
||||
});
|
||||
this.pendingGetRequests.set(init.url, {
|
||||
req,
|
||||
time: Date.now()
|
||||
});
|
||||
}
|
||||
return req;
|
||||
};
|
||||
|
||||
// FIXME : extra를 없애줘
|
||||
API.$throw = function (code, error, endpoint) {
|
||||
var text = [];
|
||||
if (code > 0) {
|
||||
const status = this.statusCodes[code];
|
||||
if (typeof status === 'undefined') {
|
||||
text.push(`${code}`);
|
||||
} else {
|
||||
const codeText = $t(`api.status_code.${code}`);
|
||||
text.push(`${code} ${codeText}`);
|
||||
}
|
||||
}
|
||||
if (typeof error !== 'undefined') {
|
||||
text.push(
|
||||
`${$t('api.error.message.error_message')}: ${typeof error === 'string' ? error : JSON.stringify(error)}`
|
||||
);
|
||||
}
|
||||
if (typeof endpoint !== 'undefined') {
|
||||
text.push(
|
||||
`${$t('api.error.message.endpoint')}: "${typeof endpoint === 'string' ? endpoint : JSON.stringify(endpoint)}"`
|
||||
);
|
||||
}
|
||||
text = text.map((s) => $app.escapeTag(s)).join('<br>');
|
||||
if (text.length) {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text
|
||||
}).show();
|
||||
}
|
||||
throw new Error(text);
|
||||
};
|
||||
|
||||
API.$bulk = function (options, args) {
|
||||
if ('handle' in options) {
|
||||
options.handle.call(this, args, options);
|
||||
}
|
||||
if (
|
||||
args.json.length > 0 &&
|
||||
((options.params.offset += args.json.length),
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
options.N > 0
|
||||
? options.N > options.params.offset
|
||||
: options.N < 0
|
||||
? args.json.length
|
||||
: options.params.n === args.json.length)
|
||||
) {
|
||||
this.bulk(options);
|
||||
} else if ('done' in options) {
|
||||
options.done.call(this, true, options);
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
API.bulk = function (options) {
|
||||
// it's stupid, but I won't waste time on the 'this' context
|
||||
// works, that's enough.
|
||||
if (typeof options.fn === 'function') {
|
||||
options
|
||||
.fn(options.params)
|
||||
.catch((err) => {
|
||||
if ('done' in options) {
|
||||
options.done.call(this, false, options);
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
.then((args) => this.$bulk(options, args));
|
||||
} else {
|
||||
this[options.fn](options.params)
|
||||
.catch((err) => {
|
||||
if ('done' in options) {
|
||||
options.done.call(this, false, options);
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
.then((args) => this.$bulk(options, args));
|
||||
}
|
||||
};
|
||||
|
||||
API.statusCodes = {
|
||||
100: 'Continue',
|
||||
101: 'Switching Protocols',
|
||||
102: 'Processing',
|
||||
103: 'Early Hints',
|
||||
200: 'OK',
|
||||
201: 'Created',
|
||||
202: 'Accepted',
|
||||
203: 'Non-Authoritative Information',
|
||||
204: 'No Content',
|
||||
205: 'Reset Content',
|
||||
206: 'Partial Content',
|
||||
207: 'Multi-Status',
|
||||
208: 'Already Reported',
|
||||
226: 'IM Used',
|
||||
300: 'Multiple Choices',
|
||||
301: 'Moved Permanently',
|
||||
302: 'Found',
|
||||
303: 'See Other',
|
||||
304: 'Not Modified',
|
||||
305: 'Use Proxy',
|
||||
306: 'Switch Proxy',
|
||||
307: 'Temporary Redirect',
|
||||
308: 'Permanent Redirect',
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Payload Too Large',
|
||||
414: 'URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
418: "I'm a teapot",
|
||||
421: 'Misdirected Request',
|
||||
422: 'Unprocessable Entity',
|
||||
423: 'Locked',
|
||||
424: 'Failed Dependency',
|
||||
425: 'Too Early',
|
||||
426: 'Upgrade Required',
|
||||
428: 'Precondition Required',
|
||||
429: 'Too Many Requests',
|
||||
431: 'Request Header Fields Too Large',
|
||||
451: 'Unavailable For Legal Reasons',
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported',
|
||||
506: 'Variant Also Negotiates',
|
||||
507: 'Insufficient Storage',
|
||||
508: 'Loop Detected',
|
||||
510: 'Not Extended',
|
||||
511: 'Network Authentication Required',
|
||||
// CloudFlare Error
|
||||
520: 'Web server returns an unknown error',
|
||||
521: 'Web server is down',
|
||||
522: 'Connection timed out',
|
||||
523: 'Origin is unreachable',
|
||||
524: 'A timeout occurred',
|
||||
525: 'SSL handshake failed',
|
||||
526: 'Invalid SSL certificate',
|
||||
527: 'Railgun Listener to origin error'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import $utils from './utils';
|
||||
/* eslint-disable no-unused-vars */
|
||||
let $app = {};
|
||||
let API = {};
|
||||
let $t = {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
class baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
$app = _app;
|
||||
API = _API;
|
||||
$t = _t;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
updateRef(_app) {
|
||||
$app = _app;
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
|
||||
export { baseClass, $app, API, $t, $utils };
|
||||
@@ -1,325 +0,0 @@
|
||||
import { isRealInstance, parseLocation } from '../composables/instance/utils';
|
||||
import { $app, API, baseClass } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.currentUser = {
|
||||
$userColour: ''
|
||||
};
|
||||
|
||||
API.getCurrentUser = function () {
|
||||
return this.call('auth/user', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json,
|
||||
fromGetCurrentUser: true
|
||||
};
|
||||
if (
|
||||
json.requiresTwoFactorAuth &&
|
||||
json.requiresTwoFactorAuth.includes('emailOtp')
|
||||
) {
|
||||
this.$emit('USER:EMAILOTP', args);
|
||||
} else if (json.requiresTwoFactorAuth) {
|
||||
this.$emit('USER:2FA', args);
|
||||
} else {
|
||||
if ($app.debugCurrentUserDiff) {
|
||||
var ref = args.json;
|
||||
var $ref = this.currentUser;
|
||||
var props = {};
|
||||
for (var prop in $ref) {
|
||||
if ($ref[prop] !== Object($ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
}
|
||||
for (var prop in ref) {
|
||||
if (
|
||||
Array.isArray(ref[prop]) &&
|
||||
Array.isArray($ref[prop])
|
||||
) {
|
||||
if (!$app.arraysMatch(ref[prop], $ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
} else if (ref[prop] !== Object(ref[prop])) {
|
||||
props[prop] = true;
|
||||
}
|
||||
}
|
||||
var has = false;
|
||||
for (var prop in props) {
|
||||
var asis = $ref[prop];
|
||||
var tobe = ref[prop];
|
||||
if (asis === tobe) {
|
||||
delete props[prop];
|
||||
} else {
|
||||
if (
|
||||
prop.startsWith('$') ||
|
||||
prop === 'offlineFriends' ||
|
||||
prop === 'onlineFriends' ||
|
||||
prop === 'activeFriends'
|
||||
) {
|
||||
delete props[prop];
|
||||
continue;
|
||||
}
|
||||
props[prop] = [tobe, asis];
|
||||
has = true;
|
||||
}
|
||||
}
|
||||
if (has) {
|
||||
console.log('API.getCurrentUser diff', props);
|
||||
}
|
||||
}
|
||||
$app.nextCurrentUserRefresh = 420; // 7mins
|
||||
this.$emit('USER:CURRENT', args);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('USER:CURRENT', function (args) {
|
||||
var { json } = args;
|
||||
args.ref = this.applyCurrentUser(json);
|
||||
|
||||
// when isGameRunning use gameLog instead of API
|
||||
var $location = parseLocation($app.lastLocation.location);
|
||||
var $travelingLocation = parseLocation(
|
||||
$app.lastLocationDestination
|
||||
);
|
||||
var location = $app.lastLocation.location;
|
||||
var instanceId = $location.instanceId;
|
||||
var worldId = $location.worldId;
|
||||
var travelingToLocation = $app.lastLocationDestination;
|
||||
var travelingToWorld = $travelingLocation.worldId;
|
||||
var travelingToInstance = $travelingLocation.instanceId;
|
||||
if (!$app.isGameRunning && json.presence) {
|
||||
if (isRealInstance(json.presence.world)) {
|
||||
location = `${json.presence.world}:${json.presence.instance}`;
|
||||
} else {
|
||||
location = json.presence.world;
|
||||
}
|
||||
if (isRealInstance(json.presence.travelingToWorld)) {
|
||||
travelingToLocation = `${json.presence.travelingToWorld}:${json.presence.travelingToInstance}`;
|
||||
} else {
|
||||
travelingToLocation = json.presence.travelingToWorld;
|
||||
}
|
||||
instanceId = json.presence.instance;
|
||||
worldId = json.presence.world;
|
||||
travelingToInstance = json.presence.travelingToInstance;
|
||||
travelingToWorld = json.presence.travelingToWorld;
|
||||
}
|
||||
this.applyUser({
|
||||
allowAvatarCopying: json.allowAvatarCopying,
|
||||
badges: json.badges,
|
||||
bio: json.bio,
|
||||
bioLinks: json.bioLinks,
|
||||
currentAvatarImageUrl: json.currentAvatarImageUrl,
|
||||
currentAvatarTags: json.currentAvatarTags,
|
||||
currentAvatarThumbnailImageUrl:
|
||||
json.currentAvatarThumbnailImageUrl,
|
||||
date_joined: json.date_joined,
|
||||
developerType: json.developerType,
|
||||
displayName: json.displayName,
|
||||
friendKey: json.friendKey,
|
||||
// json.friendRequestStatus - missing from currentUser
|
||||
id: json.id,
|
||||
// instanceId - missing from currentUser
|
||||
isFriend: json.isFriend,
|
||||
last_activity: json.last_activity,
|
||||
last_login: json.last_login,
|
||||
last_mobile: json.last_mobile,
|
||||
last_platform: json.last_platform,
|
||||
// location - missing from currentUser
|
||||
// platform - missing from currentUser
|
||||
// note - missing from currentUser
|
||||
profilePicOverride: json.profilePicOverride,
|
||||
// profilePicOverrideThumbnail - missing from currentUser
|
||||
pronouns: json.pronouns,
|
||||
state: json.state,
|
||||
status: json.status,
|
||||
statusDescription: json.statusDescription,
|
||||
tags: json.tags,
|
||||
// travelingToInstance - missing from currentUser
|
||||
// travelingToLocation - missing from currentUser
|
||||
// travelingToWorld - missing from currentUser
|
||||
userIcon: json.userIcon,
|
||||
// worldId - missing from currentUser
|
||||
fallbackAvatar: json.fallbackAvatar,
|
||||
|
||||
// Location from gameLog/presence
|
||||
location,
|
||||
instanceId,
|
||||
worldId,
|
||||
travelingToLocation,
|
||||
travelingToInstance,
|
||||
travelingToWorld,
|
||||
|
||||
// set VRCX online/offline timers
|
||||
$online_for: this.currentUser.$online_for,
|
||||
$offline_for: this.currentUser.$offline_for,
|
||||
$location_at: this.currentUser.$location_at,
|
||||
$travelingToTime: this.currentUser.$travelingToTime
|
||||
});
|
||||
});
|
||||
|
||||
API.applyCurrentUser = function (json) {
|
||||
var ref = this.currentUser;
|
||||
if (this.isLoggedIn) {
|
||||
if (json.currentAvatar !== ref.currentAvatar) {
|
||||
$app.addAvatarToHistory(json.currentAvatar);
|
||||
if ($app.isGameRunning) {
|
||||
$app.addAvatarWearTime(ref.currentAvatar);
|
||||
ref.$previousAvatarSwapTime = Date.now();
|
||||
}
|
||||
}
|
||||
Object.assign(ref, json);
|
||||
if (ref.homeLocation !== ref.$homeLocation.tag) {
|
||||
ref.$homeLocation = parseLocation(ref.homeLocation);
|
||||
// apply home location name to user dialog
|
||||
if (
|
||||
$app.userDialog.visible &&
|
||||
$app.userDialog.id === ref.id
|
||||
) {
|
||||
$app.getWorldName(API.currentUser.homeLocation).then(
|
||||
(worldName) => {
|
||||
$app.userDialog.$homeLocationName = worldName;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
ref.$isVRCPlus = ref.tags.includes('system_supporter');
|
||||
this.applyUserTrustLevel(ref);
|
||||
this.applyUserLanguage(ref);
|
||||
this.applyPresenceLocation(ref);
|
||||
this.applyQueuedInstance(ref.queuedInstance);
|
||||
this.applyPresenceGroups(ref);
|
||||
} else {
|
||||
ref = {
|
||||
acceptedPrivacyVersion: 0,
|
||||
acceptedTOSVersion: 0,
|
||||
accountDeletionDate: null,
|
||||
accountDeletionLog: null,
|
||||
activeFriends: [],
|
||||
ageVerificationStatus: '',
|
||||
ageVerified: false,
|
||||
allowAvatarCopying: false,
|
||||
badges: [],
|
||||
bio: '',
|
||||
bioLinks: [],
|
||||
currentAvatar: '',
|
||||
currentAvatarImageUrl: '',
|
||||
currentAvatarTags: [],
|
||||
currentAvatarThumbnailImageUrl: '',
|
||||
date_joined: '',
|
||||
developerType: '',
|
||||
displayName: '',
|
||||
emailVerified: false,
|
||||
fallbackAvatar: '',
|
||||
friendGroupNames: [],
|
||||
friendKey: '',
|
||||
friends: [],
|
||||
googleId: '',
|
||||
hasBirthday: false,
|
||||
hasEmail: false,
|
||||
hasLoggedInFromClient: false,
|
||||
hasPendingEmail: false,
|
||||
hideContentFilterSettings: false,
|
||||
homeLocation: '',
|
||||
id: '',
|
||||
isAdult: true,
|
||||
isBoopingEnabled: false,
|
||||
isFriend: false,
|
||||
last_activity: '',
|
||||
last_login: '',
|
||||
last_mobile: null,
|
||||
last_platform: '',
|
||||
obfuscatedEmail: '',
|
||||
obfuscatedPendingEmail: '',
|
||||
oculusId: '',
|
||||
offlineFriends: [],
|
||||
onlineFriends: [],
|
||||
pastDisplayNames: [],
|
||||
picoId: '',
|
||||
presence: {
|
||||
avatarThumbnail: '',
|
||||
currentAvatarTags: '',
|
||||
displayName: '',
|
||||
groups: [],
|
||||
id: '',
|
||||
instance: '',
|
||||
instanceType: '',
|
||||
platform: '',
|
||||
profilePicOverride: '',
|
||||
status: '',
|
||||
travelingToInstance: '',
|
||||
travelingToWorld: '',
|
||||
userIcon: '',
|
||||
world: '',
|
||||
...json.presence
|
||||
},
|
||||
profilePicOverride: '',
|
||||
pronouns: '',
|
||||
queuedInstance: '',
|
||||
state: '',
|
||||
status: '',
|
||||
statusDescription: '',
|
||||
statusFirstTime: false,
|
||||
statusHistory: [],
|
||||
steamDetails: {},
|
||||
steamId: '',
|
||||
tags: [],
|
||||
twoFactorAuthEnabled: false,
|
||||
twoFactorAuthEnabledDate: null,
|
||||
unsubscribe: false,
|
||||
updated_at: '',
|
||||
userIcon: '',
|
||||
userLanguage: '',
|
||||
userLanguageCode: '',
|
||||
username: '',
|
||||
viveId: '',
|
||||
// VRCX
|
||||
$online_for: Date.now(),
|
||||
$offline_for: '',
|
||||
$location_at: Date.now(),
|
||||
$travelingToTime: Date.now(),
|
||||
$previousAvatarSwapTime: '',
|
||||
$homeLocation: {},
|
||||
$isVRCPlus: false,
|
||||
$isModerator: false,
|
||||
$isTroll: false,
|
||||
$isProbableTroll: false,
|
||||
$trustLevel: 'Visitor',
|
||||
$trustClass: 'x-tag-untrusted',
|
||||
$userColour: '',
|
||||
$trustSortNum: 1,
|
||||
$languages: [],
|
||||
$locationTag: '',
|
||||
$travelingToLocation: '',
|
||||
...json
|
||||
};
|
||||
if ($app.isGameRunning) {
|
||||
ref.$previousAvatarSwapTime = Date.now();
|
||||
}
|
||||
ref.$homeLocation = parseLocation(ref.homeLocation);
|
||||
ref.$isVRCPlus = ref.tags.includes('system_supporter');
|
||||
this.applyUserTrustLevel(ref);
|
||||
this.applyUserLanguage(ref);
|
||||
this.applyPresenceLocation(ref);
|
||||
this.applyPresenceGroups(ref);
|
||||
this.currentUser = ref;
|
||||
this.isLoggedIn = true;
|
||||
this.$emit('LOGIN', {
|
||||
json,
|
||||
ref
|
||||
});
|
||||
}
|
||||
return ref;
|
||||
};
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
import { worldRequest } from '../api';
|
||||
import { parseLocation } from '../composables/instance/utils';
|
||||
import { getLaunchURL } from '../composables/shared/utils';
|
||||
import configRepository from '../service/config.js';
|
||||
import { API, baseClass } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
isDiscordActive: false,
|
||||
discordActive: false,
|
||||
discordInstance: true,
|
||||
discordJoinButton: false,
|
||||
discordHideInvite: true,
|
||||
discordHideImage: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
updateDiscord() {
|
||||
var currentLocation = this.lastLocation.location;
|
||||
var timeStamp = this.lastLocation.date;
|
||||
if (this.lastLocation.location === 'traveling') {
|
||||
currentLocation = this.lastLocationDestination;
|
||||
timeStamp = this.lastLocationDestinationTime;
|
||||
}
|
||||
if (
|
||||
!this.discordActive ||
|
||||
(!this.isGameRunning && !this.gameLogDisabled) ||
|
||||
(!currentLocation && !this.lastLocation$.tag)
|
||||
) {
|
||||
this.setDiscordActive(false);
|
||||
return;
|
||||
}
|
||||
this.setDiscordActive(true);
|
||||
var L = this.lastLocation$;
|
||||
if (currentLocation !== this.lastLocation$.tag) {
|
||||
Discord.SetTimestamps(timeStamp, 0);
|
||||
L = parseLocation(currentLocation);
|
||||
L.worldName = '';
|
||||
L.thumbnailImageUrl = '';
|
||||
L.worldCapacity = 0;
|
||||
L.joinUrl = '';
|
||||
L.accessName = '';
|
||||
if (L.worldId) {
|
||||
var ref = API.cachedWorlds.get(L.worldId);
|
||||
if (ref) {
|
||||
L.worldName = ref.name;
|
||||
L.thumbnailImageUrl = ref.thumbnailImageUrl;
|
||||
L.worldCapacity = ref.capacity;
|
||||
} else {
|
||||
worldRequest
|
||||
.getWorld({
|
||||
worldId: L.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
L.worldName = args.ref.name;
|
||||
L.thumbnailImageUrl =
|
||||
args.ref.thumbnailImageUrl;
|
||||
L.worldCapacity = args.ref.capacity;
|
||||
return args;
|
||||
});
|
||||
}
|
||||
if (this.isGameNoVR) {
|
||||
var platform = 'Desktop';
|
||||
} else {
|
||||
var platform = 'VR';
|
||||
}
|
||||
var groupAccessType = '';
|
||||
if (L.groupAccessType) {
|
||||
if (L.groupAccessType === 'public') {
|
||||
groupAccessType = 'Public';
|
||||
} else if (L.groupAccessType === 'plus') {
|
||||
groupAccessType = 'Plus';
|
||||
}
|
||||
}
|
||||
switch (L.accessType) {
|
||||
case 'public':
|
||||
L.joinUrl = getLaunchURL(L);
|
||||
L.accessName = `Public #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'invite+':
|
||||
L.accessName = `Invite+ #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'invite':
|
||||
L.accessName = `Invite #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'friends':
|
||||
L.accessName = `Friends #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'friends+':
|
||||
L.accessName = `Friends+ #${L.instanceName} (${platform})`;
|
||||
break;
|
||||
case 'group':
|
||||
L.accessName = `Group #${L.instanceName} (${platform})`;
|
||||
this.getGroupName(L.groupId).then((groupName) => {
|
||||
if (groupName) {
|
||||
L.accessName = `Group${groupAccessType}(${groupName}) #${L.instanceName} (${platform})`;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.lastLocation$ = L;
|
||||
}
|
||||
var hidePrivate = false;
|
||||
if (
|
||||
this.discordHideInvite &&
|
||||
(L.accessType === 'invite' ||
|
||||
L.accessType === 'invite+' ||
|
||||
L.groupAccessType === 'members')
|
||||
) {
|
||||
hidePrivate = true;
|
||||
}
|
||||
switch (API.currentUser.status) {
|
||||
case 'active':
|
||||
L.statusName = 'Online';
|
||||
L.statusImage = 'active';
|
||||
break;
|
||||
case 'join me':
|
||||
L.statusName = 'Join Me';
|
||||
L.statusImage = 'joinme';
|
||||
break;
|
||||
case 'ask me':
|
||||
L.statusName = 'Ask Me';
|
||||
L.statusImage = 'askme';
|
||||
if (this.discordHideInvite) {
|
||||
hidePrivate = true;
|
||||
}
|
||||
break;
|
||||
case 'busy':
|
||||
L.statusName = 'Do Not Disturb';
|
||||
L.statusImage = 'busy';
|
||||
hidePrivate = true;
|
||||
break;
|
||||
}
|
||||
var appId = '883308884863901717';
|
||||
var bigIcon = 'vrchat';
|
||||
var partyId = `${L.worldId}:${L.instanceName}`;
|
||||
var partySize = this.lastLocation.playerList.size;
|
||||
var partyMaxSize = L.worldCapacity;
|
||||
if (partySize > partyMaxSize) {
|
||||
partyMaxSize = partySize;
|
||||
}
|
||||
var buttonText = 'Join';
|
||||
var buttonUrl = L.joinUrl;
|
||||
if (!this.discordJoinButton) {
|
||||
buttonText = '';
|
||||
buttonUrl = '';
|
||||
}
|
||||
if (!this.discordInstance) {
|
||||
partySize = 0;
|
||||
partyMaxSize = 0;
|
||||
}
|
||||
if (hidePrivate) {
|
||||
partyId = '';
|
||||
partySize = 0;
|
||||
partyMaxSize = 0;
|
||||
buttonText = '';
|
||||
buttonUrl = '';
|
||||
} else if (this.isRpcWorld(L.tag)) {
|
||||
// custom world rpc
|
||||
if (
|
||||
L.worldId === 'wrld_f20326da-f1ac-45fc-a062-609723b097b1' ||
|
||||
L.worldId === 'wrld_10e5e467-fc65-42ed-8957-f02cace1398c' ||
|
||||
L.worldId === 'wrld_04899f23-e182-4a8d-b2c7-2c74c7c15534'
|
||||
) {
|
||||
appId = '784094509008551956';
|
||||
bigIcon = 'pypy';
|
||||
} else if (
|
||||
L.worldId === 'wrld_42377cf1-c54f-45ed-8996-5875b0573a83' ||
|
||||
L.worldId === 'wrld_dd6d2888-dbdc-47c2-bc98-3d631b2acd7c'
|
||||
) {
|
||||
appId = '846232616054030376';
|
||||
bigIcon = 'vr_dancing';
|
||||
} else if (
|
||||
L.worldId === 'wrld_52bdcdab-11cd-4325-9655-0fb120846945' ||
|
||||
L.worldId === 'wrld_2d40da63-8f1f-4011-8a9e-414eb8530acd'
|
||||
) {
|
||||
appId = '939473404808007731';
|
||||
bigIcon = 'zuwa_zuwa_dance';
|
||||
} else if (
|
||||
L.worldId === 'wrld_74970324-58e8-4239-a17b-2c59dfdf00db' ||
|
||||
L.worldId === 'wrld_db9d878f-6e76-4776-8bf2-15bcdd7fc445' ||
|
||||
L.worldId === 'wrld_435bbf25-f34f-4b8b-82c6-cd809057eb8e' ||
|
||||
L.worldId === 'wrld_f767d1c8-b249-4ecc-a56f-614e433682c8'
|
||||
) {
|
||||
appId = '968292722391785512';
|
||||
bigIcon = 'ls_media';
|
||||
} else if (
|
||||
L.worldId === 'wrld_266523e8-9161-40da-acd0-6bd82e075833'
|
||||
) {
|
||||
appId = '1095440531821170820';
|
||||
bigIcon = 'movie_and_chill';
|
||||
}
|
||||
if (this.nowPlaying.name) {
|
||||
L.worldName = this.nowPlaying.name;
|
||||
}
|
||||
if (this.nowPlaying.playing) {
|
||||
Discord.SetTimestamps(
|
||||
Date.now(),
|
||||
(this.nowPlaying.startTime -
|
||||
this.nowPlaying.offset +
|
||||
this.nowPlaying.length) *
|
||||
1000
|
||||
);
|
||||
}
|
||||
} else if (!this.discordHideImage && L.thumbnailImageUrl) {
|
||||
bigIcon = L.thumbnailImageUrl;
|
||||
}
|
||||
Discord.SetAssets(
|
||||
bigIcon, // big icon
|
||||
'Powered by VRCX', // big icon hover text
|
||||
L.statusImage, // small icon
|
||||
L.statusName, // small icon hover text
|
||||
partyId, // party id
|
||||
partySize, // party size
|
||||
partyMaxSize, // party max size
|
||||
buttonText, // button text
|
||||
buttonUrl, // button url
|
||||
appId // app id
|
||||
);
|
||||
// NOTE
|
||||
// 글자 수가 짧으면 업데이트가 안된다..
|
||||
if (L.worldName.length < 2) {
|
||||
L.worldName += '\uFFA0'.repeat(2 - L.worldName.length);
|
||||
}
|
||||
if (hidePrivate) {
|
||||
Discord.SetText('Private', '');
|
||||
Discord.SetTimestamps(0, 0);
|
||||
} else if (this.discordInstance) {
|
||||
Discord.SetText(L.worldName, L.accessName);
|
||||
} else {
|
||||
Discord.SetText(L.worldName, '');
|
||||
}
|
||||
},
|
||||
|
||||
async setDiscordActive(active) {
|
||||
if (active !== this.isDiscordActive) {
|
||||
this.isDiscordActive = await Discord.SetActive(active);
|
||||
}
|
||||
},
|
||||
|
||||
async saveDiscordOption(configLabel = '') {
|
||||
if (configLabel === 'discordActive') {
|
||||
this.discordActive = !this.discordActive;
|
||||
await configRepository.setBool(
|
||||
'discordActive',
|
||||
this.discordActive
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordInstance') {
|
||||
this.discordInstance = !this.discordInstance;
|
||||
await configRepository.setBool(
|
||||
'discordInstance',
|
||||
this.discordInstance
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordJoinButton') {
|
||||
this.discordJoinButton = !this.discordJoinButton;
|
||||
await configRepository.setBool(
|
||||
'discordJoinButton',
|
||||
this.discordJoinButton
|
||||
);
|
||||
}
|
||||
|
||||
if (configLabel === 'discordHideInvite') {
|
||||
this.discordHideInvite = !this.discordHideInvite;
|
||||
await configRepository.setBool(
|
||||
'discordHideInvite',
|
||||
this.discordHideInvite
|
||||
);
|
||||
}
|
||||
if (configLabel === 'discordHideImage') {
|
||||
this.discordHideImage = !this.discordHideImage;
|
||||
await configRepository.setBool(
|
||||
'discordHideImage',
|
||||
this.discordHideImage
|
||||
);
|
||||
}
|
||||
|
||||
this.lastLocation$.tag = '';
|
||||
this.nextDiscordUpdate = 3;
|
||||
this.updateDiscord();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import configRepository from '../service/config.js';
|
||||
import database from '../service/database.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
feedTable: {
|
||||
data: [],
|
||||
search: '',
|
||||
vip: false,
|
||||
loading: false,
|
||||
filter: [],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
},
|
||||
|
||||
feedSessionTable: []
|
||||
};
|
||||
|
||||
_methods = {
|
||||
feedSearch(row) {
|
||||
var value = this.feedTable.search.toUpperCase();
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(value.startsWith('wrld_') || value.startsWith('grp_')) &&
|
||||
String(row.location).toUpperCase().includes(value)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
switch (row.type) {
|
||||
case 'GPS':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Online':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Offline':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.worldName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Status':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.status).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
String(row.statusDescription)
|
||||
.toUpperCase()
|
||||
.includes(value)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Avatar':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.avatarName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'Bio':
|
||||
if (String(row.displayName).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.bio).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
if (String(row.previousBio).toUpperCase().includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
async feedTableLookup() {
|
||||
await configRepository.setString(
|
||||
'VRCX_feedTableFilters',
|
||||
JSON.stringify(this.feedTable.filter)
|
||||
);
|
||||
await configRepository.setBool(
|
||||
'VRCX_feedTableVIPFilter',
|
||||
this.feedTable.vip
|
||||
);
|
||||
this.feedTable.loading = true;
|
||||
var vipList = [];
|
||||
if (this.feedTable.vip) {
|
||||
vipList = Array.from(this.localFavoriteFriends.values());
|
||||
}
|
||||
this.feedTable.data = await database.lookupFeedDatabase(
|
||||
this.feedTable.search,
|
||||
this.feedTable.filter,
|
||||
vipList
|
||||
);
|
||||
this.feedTable.loading = false;
|
||||
},
|
||||
|
||||
addFeed(feed) {
|
||||
this.queueFeedNoty(feed);
|
||||
this.feedSessionTable.push(feed);
|
||||
this.updateSharedFeed(false);
|
||||
if (
|
||||
this.feedTable.filter.length > 0 &&
|
||||
!this.feedTable.filter.includes(feed.type)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.feedTable.vip &&
|
||||
!this.localFavoriteFriends.has(feed.userId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!this.feedSearch(feed)) {
|
||||
return;
|
||||
}
|
||||
this.feedTable.data.push(feed);
|
||||
this.sweepFeed();
|
||||
this.notifyMenu('feed');
|
||||
},
|
||||
|
||||
sweepFeed() {
|
||||
var { data } = this.feedTable;
|
||||
var j = data.length;
|
||||
if (j > this.maxTableSize) {
|
||||
data.splice(0, j - this.maxTableSize);
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
date.setDate(date.getDate() - 1); // 24 hour limit
|
||||
var limit = date.toJSON();
|
||||
var i = 0;
|
||||
var k = this.feedSessionTable.length;
|
||||
while (i < k && this.feedSessionTable[i].created_at < limit) {
|
||||
++i;
|
||||
}
|
||||
if (i === k) {
|
||||
this.feedSessionTable = [];
|
||||
} else if (i) {
|
||||
this.feedSessionTable.splice(0, i);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../service/config.js';
|
||||
import database from '../service/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import { inventoryRequest } from '../api';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.currentUserInventory = new Map();
|
||||
API.$on('LOGIN', function () {
|
||||
API.currentUserInventory.clear();
|
||||
});
|
||||
}
|
||||
|
||||
_data = {
|
||||
inventoryTable: []
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async getInventory() {
|
||||
this.inventoryTable = [];
|
||||
API.currentUserInventory.clear();
|
||||
var params = {
|
||||
n: 100,
|
||||
offset: 0,
|
||||
order: 'newest'
|
||||
};
|
||||
this.galleryDialogInventoryLoading = true;
|
||||
try {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
params.offset = i * params.n;
|
||||
const args =
|
||||
await inventoryRequest.getInventoryItems(params);
|
||||
for (const item of args.json.data) {
|
||||
API.currentUserInventory.set(item.id, item);
|
||||
if (!item.flags.includes('ugc')) {
|
||||
this.inventoryTable.push(item);
|
||||
}
|
||||
}
|
||||
if (args.json.data.length === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching inventory items:', error);
|
||||
} finally {
|
||||
this.galleryDialogInventoryLoading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import { userRequest } from '../api';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.$on('CONFIG', function (args) {
|
||||
var languages =
|
||||
args.ref?.constants?.LANGUAGE?.SPOKEN_LANGUAGE_OPTIONS;
|
||||
if (!languages) {
|
||||
return;
|
||||
}
|
||||
$app.subsetOfLanguages = languages;
|
||||
var data = [];
|
||||
for (var key in languages) {
|
||||
var value = languages[key];
|
||||
data.push({
|
||||
key,
|
||||
value
|
||||
});
|
||||
}
|
||||
$app.languageDialog.languages = data;
|
||||
});
|
||||
}
|
||||
|
||||
_data = {
|
||||
subsetOfLanguages: [],
|
||||
|
||||
languageDialog: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
languageChoice: false,
|
||||
languages: []
|
||||
}
|
||||
};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import database from '../service/database.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {
|
||||
hideUserMemos: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async migrateMemos() {
|
||||
var json = JSON.parse(await VRCXStorage.GetAll());
|
||||
for (var line in json) {
|
||||
if (line.substring(0, 8) === 'memo_usr') {
|
||||
var userId = line.substring(5);
|
||||
var memo = json[line];
|
||||
if (memo) {
|
||||
await this.saveUserMemo(userId, memo);
|
||||
VRCXStorage.Remove(`memo_${userId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async getUserMemo(userId) {
|
||||
try {
|
||||
return await database.getUserMemo(userId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
userId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
async saveUserMemo(id, memo) {
|
||||
if (memo) {
|
||||
await database.setUserMemo({
|
||||
userId: id,
|
||||
editedAt: new Date().toJSON(),
|
||||
memo
|
||||
});
|
||||
} else {
|
||||
await database.deleteUserMemo(id);
|
||||
}
|
||||
var ref = this.friends.get(id);
|
||||
if (ref) {
|
||||
ref.memo = String(memo || '');
|
||||
if (memo) {
|
||||
var array = memo.split('\n');
|
||||
ref.$nickName = array[0];
|
||||
} else {
|
||||
ref.$nickName = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async getAllUserMemos() {
|
||||
var memos = await database.getAllUserMemos();
|
||||
memos.forEach((memo) => {
|
||||
var ref = $app.friends.get(memo.userId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.memo = memo.memo;
|
||||
ref.$nickName = '';
|
||||
if (memo.memo) {
|
||||
var array = memo.memo.split('\n');
|
||||
ref.$nickName = array[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async getWorldMemo(worldId) {
|
||||
try {
|
||||
return await database.getWorldMemo(worldId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
worldId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
async getAvatarMemo(avatarId) {
|
||||
try {
|
||||
return await database.getAvatarMemoDB(avatarId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
avatarId: '',
|
||||
editedAt: '',
|
||||
memo: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,574 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../service/config.js';
|
||||
import database from '../service/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import { avatarRequest, favoriteRequest, worldRequest } from '../api';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_methods = {
|
||||
promptTOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
AppApi.FlashWindow();
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.totp.description'),
|
||||
$t('prompt.totp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.totp.use_otp'),
|
||||
confirmButtonText: $t('prompt.totp.verify'),
|
||||
inputPlaceholder: $t('prompt.totp.input_placeholder'),
|
||||
inputPattern: /^[0-9]{6}$/,
|
||||
inputErrorMessage: $t('prompt.totp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyTOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
$app.clearCookiesTryLogin();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.promptOTP();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.otp.description'),
|
||||
$t('prompt.otp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.otp.use_totp'),
|
||||
confirmButtonText: $t('prompt.otp.verify'),
|
||||
inputPlaceholder: $t('prompt.otp.input_placeholder'),
|
||||
inputPattern: /^[a-z0-9]{4}-[a-z0-9]{4}$/,
|
||||
inputErrorMessage: $t('prompt.otp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
$app.clearCookiesTryLogin();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.promptTOTP();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptEmailOTP() {
|
||||
if (this.twoFactorAuthDialogVisible) {
|
||||
return;
|
||||
}
|
||||
AppApi.FlashWindow();
|
||||
this.twoFactorAuthDialogVisible = true;
|
||||
this.$prompt(
|
||||
$t('prompt.email_otp.description'),
|
||||
$t('prompt.email_otp.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
cancelButtonText: $t('prompt.email_otp.resend'),
|
||||
confirmButtonText: $t('prompt.email_otp.verify'),
|
||||
inputPlaceholder: $t('prompt.email_otp.input_placeholder'),
|
||||
inputPattern: /^[0-9]{6}$/,
|
||||
inputErrorMessage: $t('prompt.email_otp.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm') {
|
||||
API.verifyEmailOTP({
|
||||
code: instance.inputValue.trim()
|
||||
})
|
||||
.catch((err) => {
|
||||
this.promptEmailOTP();
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.getCurrentUser();
|
||||
return args;
|
||||
});
|
||||
} else if (action === 'cancel') {
|
||||
this.resendEmail2fa();
|
||||
}
|
||||
},
|
||||
beforeClose: (action, instance, done) => {
|
||||
this.twoFactorAuthDialogVisible = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptOmniDirectDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.direct_access_omni.description'),
|
||||
$t('prompt.direct_access_omni.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.direct_access_omni.ok'),
|
||||
cancelButtonText: $t('prompt.direct_access_omni.cancel'),
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.direct_access_omni.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
var input = instance.inputValue.trim();
|
||||
if (!this.directAccessParse(input)) {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.direct_access_omni.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptNotificationTimeout() {
|
||||
this.$prompt(
|
||||
$t('prompt.notification_timeout.description'),
|
||||
$t('prompt.notification_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.notification_timeout.ok'),
|
||||
cancelButtonText: $t('prompt.notification_timeout.cancel'),
|
||||
inputValue: this.notificationTimeout / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.notification_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.notificationTimeout = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_notificationTimeout',
|
||||
this.notificationTimeout
|
||||
);
|
||||
this.updateVRConfigVars();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptPhotonOverlayMessageTimeout() {
|
||||
this.$prompt(
|
||||
$t('prompt.overlay_message_timeout.description'),
|
||||
$t('prompt.overlay_message_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.overlay_message_timeout.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.overlay_message_timeout.cancel'
|
||||
),
|
||||
inputValue: this.photonOverlayMessageTimeout / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.overlay_message_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.photonOverlayMessageTimeout = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_photonOverlayMessageTimeout',
|
||||
this.photonOverlayMessageTimeout
|
||||
);
|
||||
this.updateVRConfigVars();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptRenameWorld(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.rename_world.description'),
|
||||
$t('prompt.rename_world.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.rename_world.ok'),
|
||||
cancelButtonText: $t('prompt.rename_world.cancel'),
|
||||
inputValue: world.ref.name,
|
||||
inputErrorMessage: $t('prompt.rename_world.input_error'),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.name
|
||||
) {
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: world.id,
|
||||
name: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.rename_world.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldDescription(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_description.description'),
|
||||
$t('prompt.change_world_description.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_description.ok'),
|
||||
cancelButtonText: $t(
|
||||
'prompt.change_world_description.cancel'
|
||||
),
|
||||
inputValue: world.ref.description,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_description.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.description
|
||||
) {
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: world.id,
|
||||
description: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_description.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldCapacity(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_capacity.description'),
|
||||
$t('prompt.change_world_capacity.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_capacity.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_capacity.cancel'),
|
||||
inputValue: world.ref.capacity,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_capacity.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.capacity
|
||||
) {
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: world.id,
|
||||
capacity: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_capacity.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldRecommendedCapacity(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_recommended_capacity.description'),
|
||||
$t('prompt.change_world_recommended_capacity.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_capacity.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_capacity.cancel'),
|
||||
inputValue: world.ref.recommendedCapacity,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_recommended_capacity.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !==
|
||||
world.ref.recommendedCapacity
|
||||
) {
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: world.id,
|
||||
recommendedCapacity: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_recommended_capacity.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptChangeWorldYouTubePreview(world) {
|
||||
this.$prompt(
|
||||
$t('prompt.change_world_preview.description'),
|
||||
$t('prompt.change_world_preview.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_world_preview.ok'),
|
||||
cancelButtonText: $t('prompt.change_world_preview.cancel'),
|
||||
inputValue: world.ref.previewYoutubeId,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_world_preview.input_error'
|
||||
),
|
||||
callback: (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue !== world.ref.previewYoutubeId
|
||||
) {
|
||||
if (instance.inputValue.length > 11) {
|
||||
try {
|
||||
var url = new URL(instance.inputValue);
|
||||
var id1 = url.pathname;
|
||||
var id2 = url.searchParams.get('v');
|
||||
if (id1 && id1.length === 12) {
|
||||
instance.inputValue = id1.substring(
|
||||
1,
|
||||
12
|
||||
);
|
||||
}
|
||||
if (id2 && id2.length === 11) {
|
||||
instance.inputValue = id2;
|
||||
}
|
||||
} catch {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_preview.message.error'
|
||||
),
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (
|
||||
instance.inputValue !==
|
||||
world.ref.previewYoutubeId
|
||||
) {
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: world.id,
|
||||
previewYoutubeId: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: $t(
|
||||
'prompt.change_world_preview.message.success'
|
||||
),
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptMaxTableSizeDialog() {
|
||||
this.$prompt(
|
||||
$t('prompt.change_table_size.description'),
|
||||
$t('prompt.change_table_size.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.change_table_size.save'),
|
||||
cancelButtonText: $t('prompt.change_table_size.cancel'),
|
||||
inputValue: this.maxTableSize,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.change_table_size.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (action === 'confirm' && instance.inputValue) {
|
||||
if (instance.inputValue > 10000) {
|
||||
instance.inputValue = 10000;
|
||||
}
|
||||
this.maxTableSize = instance.inputValue;
|
||||
await configRepository.setString(
|
||||
'VRCX_maxTableSize',
|
||||
this.maxTableSize
|
||||
);
|
||||
database.setmaxTableSize(this.maxTableSize);
|
||||
this.feedTableLookup();
|
||||
this.gameLogTableLookup();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptProxySettings() {
|
||||
this.$prompt(
|
||||
$t('prompt.proxy_settings.description'),
|
||||
$t('prompt.proxy_settings.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.proxy_settings.restart'),
|
||||
cancelButtonText: $t('prompt.proxy_settings.close'),
|
||||
inputValue: this.proxyServer,
|
||||
inputPlaceholder: $t('prompt.proxy_settings.placeholder'),
|
||||
callback: async (action, instance) => {
|
||||
this.proxyServer = instance.inputValue;
|
||||
await VRCXStorage.Set(
|
||||
'VRCX_ProxyServer',
|
||||
this.proxyServer
|
||||
);
|
||||
await VRCXStorage.Flush();
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 100);
|
||||
});
|
||||
if (action === 'confirm') {
|
||||
var isUpgrade = false;
|
||||
this.restartVRCX(isUpgrade);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptPhotonLobbyTimeoutThreshold() {
|
||||
this.$prompt(
|
||||
$t('prompt.photon_lobby_timeout.description'),
|
||||
$t('prompt.photon_lobby_timeout.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.photon_lobby_timeout.ok'),
|
||||
cancelButtonText: $t('prompt.photon_lobby_timeout.cancel'),
|
||||
inputValue: this.photonLobbyTimeoutThreshold / 1000,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.photon_lobby_timeout.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.photonLobbyTimeoutThreshold = Math.trunc(
|
||||
Number(instance.inputValue) * 1000
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_photonLobbyTimeoutThreshold',
|
||||
this.photonLobbyTimeoutThreshold
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
promptAutoClearVRCXCacheFrequency() {
|
||||
this.$prompt(
|
||||
$t('prompt.auto_clear_cache.description'),
|
||||
$t('prompt.auto_clear_cache.header'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: $t('prompt.auto_clear_cache.ok'),
|
||||
cancelButtonText: $t('prompt.auto_clear_cache.cancel'),
|
||||
inputValue: this.clearVRCXCacheFrequency / 3600 / 2,
|
||||
inputPattern: /\d+$/,
|
||||
inputErrorMessage: $t(
|
||||
'prompt.auto_clear_cache.input_error'
|
||||
),
|
||||
callback: async (action, instance) => {
|
||||
if (
|
||||
action === 'confirm' &&
|
||||
instance.inputValue &&
|
||||
!isNaN(instance.inputValue)
|
||||
) {
|
||||
this.clearVRCXCacheFrequency = Math.trunc(
|
||||
Number(instance.inputValue) * 3600 * 2
|
||||
);
|
||||
await configRepository.setString(
|
||||
'VRCX_clearVRCXCacheFrequency',
|
||||
this.clearVRCXCacheFrequency
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import configRepository from '../service/config.js';
|
||||
import database from '../service/database.js';
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {
|
||||
async tryRestoreFriendNumber() {
|
||||
var lastUpdate = await configRepository.getString(
|
||||
`VRCX_lastStoreTime_${API.currentUser.id}`
|
||||
);
|
||||
if (lastUpdate == -4) {
|
||||
// this means the backup was already applied
|
||||
return;
|
||||
}
|
||||
var status = false;
|
||||
this.friendNumber = 0;
|
||||
for (var ref of this.friendLog.values()) {
|
||||
ref.friendNumber = 0;
|
||||
}
|
||||
try {
|
||||
if (lastUpdate) {
|
||||
// backup ready to try apply
|
||||
status = await this.restoreFriendNumber();
|
||||
}
|
||||
// needs to be in reverse because we don't know the starting number
|
||||
this.applyFriendLogFriendOrderInReverse();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
// if (status) {
|
||||
// this.$message({
|
||||
// message: 'Friend order restored from backup',
|
||||
// type: 'success',
|
||||
// duration: 0,
|
||||
// showClose: true
|
||||
// });
|
||||
// } else if (this.friendLogTable.data.length > 0) {
|
||||
// this.$message({
|
||||
// message:
|
||||
// 'No backup found, friend order partially restored from friendLog',
|
||||
// type: 'success',
|
||||
// duration: 0,
|
||||
// showClose: true
|
||||
// });
|
||||
// }
|
||||
await configRepository.setString(
|
||||
`VRCX_lastStoreTime_${API.currentUser.id}`,
|
||||
-4
|
||||
);
|
||||
},
|
||||
|
||||
async restoreFriendNumber() {
|
||||
var storedData = null;
|
||||
try {
|
||||
var data = await configRepository.getString(
|
||||
`VRCX_friendOrder_${API.currentUser.id}`
|
||||
);
|
||||
if (data) {
|
||||
var storedData = JSON.parse(data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
if (!storedData || storedData.length === 0) {
|
||||
var message = 'whomp whomp, no friend order backup found';
|
||||
console.error(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
|
||||
// for storedData
|
||||
var machList = [];
|
||||
for (var i = 0; i < Object.keys(storedData).length; i++) {
|
||||
var key = Object.keys(storedData)[i];
|
||||
var value = storedData[key];
|
||||
var item = this.parseFriendOrderBackup(
|
||||
friendLogTable,
|
||||
key,
|
||||
value
|
||||
);
|
||||
machList.push(item);
|
||||
}
|
||||
machList.sort((a, b) => b.matches - a.matches);
|
||||
console.log(
|
||||
`friendLog: ${friendLogTable.length} friendOrderBackups:`,
|
||||
machList
|
||||
);
|
||||
|
||||
var bestBackup = machList[0];
|
||||
if (!bestBackup?.isValid) {
|
||||
var message = 'whomp whomp, no valid backup found';
|
||||
console.error(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.applyFriendOrderBackup(bestBackup.table);
|
||||
this.applyFriendLogFriendOrder();
|
||||
await configRepository.setInt(
|
||||
`VRCX_friendNumber_${API.currentUser.id}`,
|
||||
this.friendNumber
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
getFriendLogFriendOrder() {
|
||||
var friendLogTable = [];
|
||||
for (var i = 0; i < this.friendLogTable.data.length; i++) {
|
||||
var ref = this.friendLogTable.data[i];
|
||||
if (ref.type !== 'Friend') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
friendLogTable.findIndex((x) => x.id === ref.userId) !== -1
|
||||
) {
|
||||
// console.log(
|
||||
// 'ignoring duplicate friend',
|
||||
// ref.displayName,
|
||||
// ref.created_at
|
||||
// );
|
||||
continue;
|
||||
}
|
||||
friendLogTable.push({
|
||||
id: ref.userId,
|
||||
displayName: ref.displayName,
|
||||
created_at: ref.created_at
|
||||
});
|
||||
}
|
||||
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;
|
||||
};
|
||||
friendLogTable.sort(compareByCreatedAt);
|
||||
return friendLogTable;
|
||||
},
|
||||
|
||||
applyFriendLogFriendOrder() {
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
if (this.friendNumber === 0) {
|
||||
console.log(
|
||||
'No backup applied, applying friend log in reverse'
|
||||
);
|
||||
// this means no FriendOrderBackup was applied
|
||||
// will need to apply in reverse order instead
|
||||
return;
|
||||
}
|
||||
for (var friendLog of friendLogTable) {
|
||||
var ref = this.friendLog.get(friendLog.id);
|
||||
if (!ref || ref.friendNumber) {
|
||||
continue;
|
||||
}
|
||||
ref.friendNumber = ++this.friendNumber;
|
||||
this.friendLog.set(ref.userId, ref);
|
||||
database.setFriendLogCurrent(ref);
|
||||
var friendRef = this.friends.get(friendLog.id);
|
||||
if (friendRef?.ref) {
|
||||
friendRef.ref.$friendNumber = ref.friendNumber;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyFriendLogFriendOrderInReverse() {
|
||||
this.friendNumber = this.friends.size + 1;
|
||||
var friendLogTable = this.getFriendLogFriendOrder();
|
||||
for (var i = friendLogTable.length - 1; i > -1; i--) {
|
||||
var friendLog = friendLogTable[i];
|
||||
var ref = this.friendLog.get(friendLog.id);
|
||||
if (!ref) {
|
||||
continue;
|
||||
}
|
||||
if (ref.friendNumber) {
|
||||
break;
|
||||
}
|
||||
ref.friendNumber = --this.friendNumber;
|
||||
this.friendLog.set(ref.userId, ref);
|
||||
database.setFriendLogCurrent(ref);
|
||||
var friendRef = this.friends.get(friendLog.id);
|
||||
if (friendRef?.ref) {
|
||||
friendRef.ref.$friendNumber = ref.friendNumber;
|
||||
}
|
||||
}
|
||||
this.friendNumber = this.friends.size;
|
||||
console.log('Applied friend order from friendLog');
|
||||
},
|
||||
|
||||
parseFriendOrderBackup(friendLogTable, created_at, backupUserIds) {
|
||||
var backupTable = [];
|
||||
for (var i = 0; i < backupUserIds.length; i++) {
|
||||
var userId = backupUserIds[i];
|
||||
var ctx = this.friends.get(userId);
|
||||
if (ctx) {
|
||||
backupTable.push({
|
||||
id: ctx.id,
|
||||
displayName: ctx.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// var compareTable = [];
|
||||
// compare 2 tables, find max amount of id's in same order
|
||||
var maxMatches = 0;
|
||||
var currentMatches = 0;
|
||||
var backupIndex = 0;
|
||||
for (var i = 0; i < friendLogTable.length; i++) {
|
||||
var isMatch = false;
|
||||
var ref = friendLogTable[i];
|
||||
if (backupIndex <= 0) {
|
||||
backupIndex = backupTable.findIndex((x) => x.id === ref.id);
|
||||
if (backupIndex !== -1) {
|
||||
currentMatches = 1;
|
||||
}
|
||||
} else if (backupTable[backupIndex].id === ref.id) {
|
||||
currentMatches++;
|
||||
isMatch = true;
|
||||
} else {
|
||||
var backupIndex = backupTable.findIndex(
|
||||
(x) => x.id === ref.id
|
||||
);
|
||||
if (backupIndex !== -1) {
|
||||
currentMatches = 1;
|
||||
}
|
||||
}
|
||||
if (backupIndex === backupTable.length - 1) {
|
||||
backupIndex = 0;
|
||||
} else {
|
||||
backupIndex++;
|
||||
}
|
||||
if (currentMatches > maxMatches) {
|
||||
maxMatches = currentMatches;
|
||||
}
|
||||
// compareTable.push({
|
||||
// id: ref.id,
|
||||
// displayName: ref.displayName,
|
||||
// match: isMatch
|
||||
// });
|
||||
}
|
||||
|
||||
var lerp = (a, b, alpha) => {
|
||||
return a + alpha * (b - a);
|
||||
};
|
||||
return {
|
||||
matches: parseFloat(`${maxMatches}.${created_at}`),
|
||||
table: backupUserIds,
|
||||
isValid: maxMatches > lerp(4, 10, backupTable.length / 1000) // pls no collisions
|
||||
};
|
||||
},
|
||||
|
||||
applyFriendOrderBackup(userIdOrder) {
|
||||
for (var i = 0; i < userIdOrder.length; i++) {
|
||||
var userId = userIdOrder[i];
|
||||
var ctx = this.friends.get(userId);
|
||||
var ref = ctx?.ref;
|
||||
if (!ref || ref.$friendNumber) {
|
||||
continue;
|
||||
}
|
||||
var friendLogCurrent = {
|
||||
userId,
|
||||
displayName: ref.displayName,
|
||||
trustLevel: ref.$trustLevel,
|
||||
friendNumber: i + 1
|
||||
};
|
||||
this.friendLog.set(userId, friendLogCurrent);
|
||||
database.setFriendLogCurrent(friendLogCurrent);
|
||||
this.friendNumber = i + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,577 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { baseClass, $app, API } from './baseClass.js';
|
||||
import { worldRequest, groupRequest } from '../api';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
sharedFeed: {
|
||||
gameLog: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
feedTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
notificationTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
friendLogTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
moderationAgainstTable: {
|
||||
wrist: [],
|
||||
lastEntryDate: ''
|
||||
},
|
||||
pendingUpdate: false
|
||||
},
|
||||
updateSharedFeedTimer: null,
|
||||
updateSharedFeedPending: false,
|
||||
updateSharedFeedPendingForceUpdate: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
updateSharedFeed(forceUpdate) {
|
||||
if (!this.friendLogInitStatus) {
|
||||
return;
|
||||
}
|
||||
if (this.updateSharedFeedTimer) {
|
||||
if (forceUpdate) {
|
||||
this.updateSharedFeedPendingForceUpdate = true;
|
||||
}
|
||||
this.updateSharedFeedPending = true;
|
||||
} else {
|
||||
this.updateSharedExecute(forceUpdate);
|
||||
this.updateSharedFeedTimer = setTimeout(() => {
|
||||
if (this.updateSharedFeedPending) {
|
||||
this.updateSharedExecute(
|
||||
this.updateSharedFeedPendingForceUpdate
|
||||
);
|
||||
}
|
||||
this.updateSharedFeedTimer = null;
|
||||
}, 150);
|
||||
}
|
||||
},
|
||||
|
||||
updateSharedExecute(forceUpdate) {
|
||||
try {
|
||||
this.updateSharedFeedDebounce(forceUpdate);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
this.updateSharedFeedTimer = null;
|
||||
this.updateSharedFeedPending = false;
|
||||
this.updateSharedFeedPendingForceUpdate = false;
|
||||
},
|
||||
|
||||
updateSharedFeedDebounce(forceUpdate) {
|
||||
this.updateSharedFeedGameLog(forceUpdate);
|
||||
this.updateSharedFeedFeedTable(forceUpdate);
|
||||
this.updateSharedFeedNotificationTable(forceUpdate);
|
||||
this.updateSharedFeedFriendLogTable(forceUpdate);
|
||||
this.updateSharedFeedModerationAgainstTable(forceUpdate);
|
||||
var feeds = this.sharedFeed;
|
||||
if (!feeds.pendingUpdate) {
|
||||
return;
|
||||
}
|
||||
var wristFeed = [];
|
||||
wristFeed = wristFeed.concat(
|
||||
feeds.gameLog.wrist,
|
||||
feeds.feedTable.wrist,
|
||||
feeds.notificationTable.wrist,
|
||||
feeds.friendLogTable.wrist,
|
||||
feeds.moderationAgainstTable.wrist
|
||||
);
|
||||
// OnPlayerJoining/Traveling
|
||||
API.currentTravelers.forEach((ref) => {
|
||||
var isFavorite = this.localFavoriteFriends.has(ref.id);
|
||||
if (
|
||||
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
|
||||
'Friends' ||
|
||||
(this.sharedFeedFilters.wrist.OnPlayerJoining ===
|
||||
'VIP' &&
|
||||
isFavorite)) &&
|
||||
!$app.lastLocation.playerList.has(ref.id)
|
||||
) {
|
||||
if (ref.$location.tag === $app.lastLocation.location) {
|
||||
var feedEntry = {
|
||||
...ref,
|
||||
isFavorite,
|
||||
isFriend: true,
|
||||
type: 'OnPlayerJoining'
|
||||
};
|
||||
wristFeed.unshift(feedEntry);
|
||||
} else {
|
||||
var worldRef = API.cachedWorlds.get(
|
||||
ref.$location.worldId
|
||||
);
|
||||
var groupName = '';
|
||||
if (ref.$location.groupId) {
|
||||
var groupRef = API.cachedGroups.get(
|
||||
ref.$location.groupId
|
||||
);
|
||||
if (typeof groupRef !== 'undefined') {
|
||||
groupName = groupRef.name;
|
||||
} else {
|
||||
// no group cache, fetch group and try again
|
||||
groupRequest
|
||||
.getGroup({
|
||||
groupId: ref.$location.groupId
|
||||
})
|
||||
.then((args) => {
|
||||
workerTimers.setTimeout(() => {
|
||||
// delay to allow for group cache to update
|
||||
$app.sharedFeed.pendingUpdate =
|
||||
true;
|
||||
$app.updateSharedFeed(false);
|
||||
}, 100);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (typeof worldRef !== 'undefined') {
|
||||
var feedEntry = {
|
||||
created_at: ref.created_at,
|
||||
type: 'GPS',
|
||||
userId: ref.id,
|
||||
displayName: ref.displayName,
|
||||
location: ref.$location.tag,
|
||||
worldName: worldRef.name,
|
||||
groupName,
|
||||
previousLocation: '',
|
||||
isFavorite,
|
||||
time: 0,
|
||||
isFriend: true,
|
||||
isTraveling: true
|
||||
};
|
||||
wristFeed.unshift(feedEntry);
|
||||
} else {
|
||||
// no world cache, fetch world and try again
|
||||
worldRequest
|
||||
.getWorld({
|
||||
worldId: ref.$location.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
workerTimers.setTimeout(() => {
|
||||
// delay to allow for world cache to update
|
||||
$app.sharedFeed.pendingUpdate = true;
|
||||
$app.updateSharedFeed(false);
|
||||
}, 100);
|
||||
return args;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
wristFeed.sort(function (a, b) {
|
||||
if (a.created_at < b.created_at) {
|
||||
return 1;
|
||||
}
|
||||
if (a.created_at > b.created_at) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
wristFeed.splice(16);
|
||||
AppApi.ExecuteVrFeedFunction(
|
||||
'wristFeedUpdate',
|
||||
JSON.stringify(wristFeed)
|
||||
);
|
||||
this.applyUserDialogLocation();
|
||||
this.applyWorldDialogInstances();
|
||||
this.applyGroupDialogInstances();
|
||||
feeds.pendingUpdate = false;
|
||||
},
|
||||
|
||||
updateSharedFeedGameLog(forceUpdate) {
|
||||
// Location, OnPlayerJoined, OnPlayerLeft
|
||||
var sessionTable = this.gameLogSessionTable;
|
||||
var i = sessionTable.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
sessionTable[i - 1].created_at ===
|
||||
this.sharedFeed.gameLog.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.gameLog.lastEntryDate =
|
||||
sessionTable[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
var currentUserLeaveTime = 0;
|
||||
var locationJoinTime = 0;
|
||||
for (var i = sessionTable.length - 1; i > -1; i--) {
|
||||
var ctx = sessionTable[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'Notification') {
|
||||
continue;
|
||||
}
|
||||
// on Location change remove OnPlayerLeft
|
||||
if (ctx.type === 'LocationDestination') {
|
||||
currentUserLeaveTime = Date.parse(ctx.created_at);
|
||||
var currentUserLeaveTimeOffset =
|
||||
currentUserLeaveTime + 5 * 1000;
|
||||
for (var k = w - 1; k > -1; k--) {
|
||||
var feedItem = wristArr[k];
|
||||
if (
|
||||
(feedItem.type === 'OnPlayerLeft' ||
|
||||
feedItem.type === 'BlockedOnPlayerLeft' ||
|
||||
feedItem.type === 'MutedOnPlayerLeft') &&
|
||||
Date.parse(feedItem.created_at) >=
|
||||
currentUserLeaveTime &&
|
||||
Date.parse(feedItem.created_at) <=
|
||||
currentUserLeaveTimeOffset
|
||||
) {
|
||||
wristArr.splice(k, 1);
|
||||
w--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// on Location change remove OnPlayerJoined
|
||||
if (ctx.type === 'Location') {
|
||||
locationJoinTime = Date.parse(ctx.created_at);
|
||||
var locationJoinTimeOffset = locationJoinTime + 20 * 1000;
|
||||
for (var k = w - 1; k > -1; k--) {
|
||||
var feedItem = wristArr[k];
|
||||
if (
|
||||
(feedItem.type === 'OnPlayerJoined' ||
|
||||
feedItem.type === 'BlockedOnPlayerJoined' ||
|
||||
feedItem.type === 'MutedOnPlayerJoined') &&
|
||||
Date.parse(feedItem.created_at) >=
|
||||
locationJoinTime &&
|
||||
Date.parse(feedItem.created_at) <=
|
||||
locationJoinTimeOffset
|
||||
) {
|
||||
wristArr.splice(k, 1);
|
||||
w--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove current user
|
||||
if (
|
||||
(ctx.type === 'OnPlayerJoined' ||
|
||||
ctx.type === 'OnPlayerLeft' ||
|
||||
ctx.type === 'PortalSpawn') &&
|
||||
ctx.displayName === API.currentUser.displayName
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = false;
|
||||
var isFavorite = false;
|
||||
if (ctx.userId) {
|
||||
isFriend = this.friends.has(ctx.userId);
|
||||
isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
} else if (ctx.displayName) {
|
||||
for (var ref of API.cachedUsers.values()) {
|
||||
if (ref.displayName === ctx.displayName) {
|
||||
isFriend = this.friends.has(ref.id);
|
||||
isFavorite = this.localFavoriteFriends.has(ref.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// add tag colour
|
||||
var tagColour = '';
|
||||
if (ctx.userId) {
|
||||
var tagRef = this.customUserTags.get(ctx.userId);
|
||||
if (typeof tagRef !== 'undefined') {
|
||||
tagColour = tagRef.colour;
|
||||
}
|
||||
}
|
||||
// BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft
|
||||
if (
|
||||
ctx.type === 'OnPlayerJoined' ||
|
||||
ctx.type === 'OnPlayerLeft'
|
||||
) {
|
||||
for (var ref of API.cachedPlayerModerations.values()) {
|
||||
if (
|
||||
ref.targetDisplayName !== ctx.displayName &&
|
||||
ref.sourceUserId !== ctx.userId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ref.type === 'block') {
|
||||
var type = `Blocked${ctx.type}`;
|
||||
} else if (ref.type === 'mute') {
|
||||
var type = `Muted${ctx.type}`;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
var entry = {
|
||||
created_at: ctx.created_at,
|
||||
type,
|
||||
displayName: ref.targetDisplayName,
|
||||
userId: ref.targetUserId,
|
||||
isFriend,
|
||||
isFavorite
|
||||
};
|
||||
if (
|
||||
wristFilter[type] &&
|
||||
(wristFilter[type] === 'Everyone' ||
|
||||
(wristFilter[type] === 'Friends' && isFriend) ||
|
||||
(wristFilter[type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.unshift(entry);
|
||||
}
|
||||
this.queueGameLogNoty(entry);
|
||||
}
|
||||
}
|
||||
// when too many user joins happen at once when switching instances
|
||||
// the "w" counter maxes out and wont add any more entries
|
||||
// until the onJoins are cleared by "Location"
|
||||
// e.g. if a "VideoPlay" occurs between "OnPlayerJoined" and "Location" it wont be added
|
||||
if (
|
||||
w < 50 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Everyone' ||
|
||||
(wristFilter[ctx.type] === 'Friends' && isFriend) ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
tagColour,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.gameLog.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedFeedTable(forceUpdate) {
|
||||
// GPS, Online, Offline, Status, Avatar
|
||||
var feedSession = this.feedSessionTable;
|
||||
var i = feedSession.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
feedSession[i - 1].created_at ===
|
||||
this.sharedFeed.feedTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.feedTable.lastEntryDate =
|
||||
feedSession[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = feedSession.length - 1; i > -1; i--) {
|
||||
var ctx = feedSession[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'Avatar') {
|
||||
continue;
|
||||
}
|
||||
// hide private worlds from feed
|
||||
if (
|
||||
this.hidePrivateFromFeed &&
|
||||
ctx.type === 'GPS' &&
|
||||
ctx.location === 'private'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.feedTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedNotificationTable(forceUpdate) {
|
||||
// invite, requestInvite, requestInviteResponse, inviteResponse, friendRequest
|
||||
var notificationTable = this.notificationTable.data;
|
||||
var i = notificationTable.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
notificationTable[i - 1].created_at ===
|
||||
this.sharedFeed.notificationTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.notificationTable.lastEntryDate =
|
||||
notificationTable[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = notificationTable.length - 1; i > -1; i--) {
|
||||
var ctx = notificationTable[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.senderUserId === API.currentUser.id) {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.senderUserId);
|
||||
var isFavorite = this.localFavoriteFriends.has(
|
||||
ctx.senderUserId
|
||||
);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.notificationTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedFriendLogTable(forceUpdate) {
|
||||
// TrustLevel, Friend, FriendRequest, Unfriend, DisplayName
|
||||
var friendLog = this.friendLogTable.data;
|
||||
var i = friendLog.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
friendLog[i - 1].created_at ===
|
||||
this.sharedFeed.friendLogTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.friendLogTable.lastEntryDate =
|
||||
friendLog[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = friendLog.length - 1; i > -1; i--) {
|
||||
var ctx = friendLog[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
if (ctx.type === 'FriendRequest') {
|
||||
continue;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
(wristFilter[ctx.type] === 'On' ||
|
||||
wristFilter[ctx.type] === 'Friends' ||
|
||||
(wristFilter[ctx.type] === 'VIP' && isFavorite))
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.friendLogTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
},
|
||||
|
||||
updateSharedFeedModerationAgainstTable(forceUpdate) {
|
||||
// Unblocked, Blocked, Muted, Unmuted
|
||||
var moderationAgainst = this.moderationAgainstTable;
|
||||
var i = moderationAgainst.length;
|
||||
if (i > 0) {
|
||||
if (
|
||||
moderationAgainst[i - 1].created_at ===
|
||||
this.sharedFeed.moderationAgainstTable.lastEntryDate &&
|
||||
forceUpdate === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.sharedFeed.moderationAgainstTable.lastEntryDate =
|
||||
moderationAgainst[i - 1].created_at;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
|
||||
var wristArr = [];
|
||||
var w = 0;
|
||||
var wristFilter = this.sharedFeedFilters.wrist;
|
||||
for (var i = moderationAgainst.length - 1; i > -1; i--) {
|
||||
var ctx = moderationAgainst[i];
|
||||
if (ctx.created_at < bias) {
|
||||
break;
|
||||
}
|
||||
var isFriend = this.friends.has(ctx.userId);
|
||||
var isFavorite = this.localFavoriteFriends.has(ctx.userId);
|
||||
// add tag colour
|
||||
var tagColour = '';
|
||||
var tagRef = this.customUserTags.get(ctx.userId);
|
||||
if (typeof tagRef !== 'undefined') {
|
||||
tagColour = tagRef.colour;
|
||||
}
|
||||
if (
|
||||
w < 20 &&
|
||||
wristFilter[ctx.type] &&
|
||||
wristFilter[ctx.type] === 'On'
|
||||
) {
|
||||
wristArr.push({
|
||||
...ctx,
|
||||
isFriend,
|
||||
isFavorite,
|
||||
tagColour
|
||||
});
|
||||
++w;
|
||||
}
|
||||
}
|
||||
this.sharedFeed.moderationAgainstTable.wrist = wristArr;
|
||||
this.sharedFeed.pendingUpdate = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,512 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import VueMarkdown from 'vue-markdown';
|
||||
import { instanceRequest, userRequest } from '../api';
|
||||
import { hasGroupPermission } from '../composables/group/utils';
|
||||
import { parseLocation } from '../composables/instance/utils';
|
||||
import { $app, $t, API, baseClass } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
Vue.component('vue-markdown', VueMarkdown);
|
||||
|
||||
Vue.component('launch', {
|
||||
template:
|
||||
'<el-tooltip placement="top" :content="$t(`dialog.user.info.launch_invite_tooltip`)" :disabled="hideTooltips"><el-button @click="confirm" size="mini" icon="el-icon-switch-button" circle></el-button></el-tooltip>',
|
||||
props: {
|
||||
location: String,
|
||||
hideTooltips: Boolean
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.$el.style.display = $app.checkCanInviteSelf(
|
||||
this.location
|
||||
)
|
||||
? ''
|
||||
: 'none';
|
||||
},
|
||||
confirm() {
|
||||
this.$emit('show-launch-dialog', this.location);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('invite-yourself', {
|
||||
template:
|
||||
'<el-button @click="confirm" size="mini" icon="el-icon-message" circle></el-button>',
|
||||
props: {
|
||||
location: String,
|
||||
shortname: String
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.$el.style.display = $app.checkCanInviteSelf(
|
||||
this.location
|
||||
)
|
||||
? ''
|
||||
: 'none';
|
||||
},
|
||||
confirm() {
|
||||
this.selfInvite(this.location, this.shortname);
|
||||
},
|
||||
selfInvite(location, shortName) {
|
||||
const L = parseLocation(location);
|
||||
if (!L.isRealInstance) {
|
||||
return;
|
||||
}
|
||||
instanceRequest
|
||||
.selfInvite({
|
||||
instanceId: L.instanceId,
|
||||
worldId: L.worldId,
|
||||
shortName
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: 'Self invite sent',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('location-world', {
|
||||
template:
|
||||
'<span><span @click="showLaunchDialog" class="x-link">' +
|
||||
'<i v-if="isUnlocked" class="el-icon el-icon-unlock" style="display:inline-block;margin-right:5px"></i>' +
|
||||
'<span>#{{ instanceName }} {{ accessTypeName }}</span></span>' +
|
||||
'<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span>' +
|
||||
'<span class="flags" :class="region" style="display:inline-block;margin-left:5px"></span>' +
|
||||
'<i v-if="strict" class="el-icon el-icon-lock" style="display:inline-block;margin-left:5px"></i></span>',
|
||||
props: {
|
||||
locationobject: Object,
|
||||
currentuserid: String,
|
||||
worlddialogshortname: String,
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
location: this.location,
|
||||
instanceName: this.instanceName,
|
||||
accessTypeName: this.accessTypeName,
|
||||
region: this.region,
|
||||
shortName: this.shortName,
|
||||
isUnlocked: this.isUnlocked,
|
||||
strict: this.strict,
|
||||
groupName: this.groupName
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.location = this.locationobject.tag;
|
||||
this.instanceName = this.locationobject.instanceName;
|
||||
this.accessTypeName = this.locationobject.accessTypeName;
|
||||
this.strict = this.locationobject.strict;
|
||||
this.shortName = this.locationobject.shortName;
|
||||
|
||||
this.isUnlocked = false;
|
||||
if (
|
||||
(this.worlddialogshortname &&
|
||||
this.locationobject.shortName &&
|
||||
this.worlddialogshortname ===
|
||||
this.locationobject.shortName) ||
|
||||
this.currentuserid === this.locationobject.userId
|
||||
) {
|
||||
this.isUnlocked = true;
|
||||
}
|
||||
|
||||
this.region = this.locationobject.region;
|
||||
if (!this.region) {
|
||||
this.region = 'us';
|
||||
}
|
||||
|
||||
this.groupName = '';
|
||||
if (this.grouphint) {
|
||||
this.groupName = this.grouphint;
|
||||
} else if (this.locationobject.groupId) {
|
||||
this.groupName = this.locationobject.groupId;
|
||||
$app.getGroupName(this.locationobject.groupId).then(
|
||||
(groupName) => {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
showLaunchDialog() {
|
||||
this.$emit(
|
||||
'show-launch-dialog',
|
||||
this.location,
|
||||
this.shortName
|
||||
);
|
||||
},
|
||||
showGroupDialog() {
|
||||
if (!this.location) {
|
||||
return;
|
||||
}
|
||||
var L = parseLocation(this.location);
|
||||
if (!L.groupId) {
|
||||
return;
|
||||
}
|
||||
$app.showGroupDialog(L.groupId);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
locationobject() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('last-join', {
|
||||
template:
|
||||
'<span v-if="lastJoin">' +
|
||||
'<el-tooltip placement="top" style="margin-left:5px" >' +
|
||||
'<div slot="content">' +
|
||||
'<span>{{ $t("dialog.user.info.last_join") }} <timer :epoch="lastJoin"></timer></span>' +
|
||||
'</div>' +
|
||||
'<i class="el-icon el-icon-location-outline" style="display:inline-block"></i>' +
|
||||
'</el-tooltip>' +
|
||||
'</span>',
|
||||
props: {
|
||||
location: String,
|
||||
currentlocation: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastJoin: this.lastJoin
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.lastJoin = $app.instanceJoinHistory.get(this.location);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
currentlocation() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('instance-info', {
|
||||
template:
|
||||
'<div style="display:inline-block;margin-left:5px">' +
|
||||
'<el-tooltip v-if="isValidInstance" placement="bottom">' +
|
||||
'<div slot="content">' +
|
||||
'<template v-if="isClosed"><span>Closed At: {{ closedAt | formatDate(\'long\') }}</span></br></template>' +
|
||||
'<template v-if="canCloseInstance"><el-button :disabled="isClosed" size="mini" type="primary" @click="$root.closeInstance(location)">{{ $t("dialog.user.info.close_instance") }}</el-button></br></br></template>' +
|
||||
'<span><span style="color:#409eff">PC: </span>{{ platforms.standalonewindows }}</span></br>' +
|
||||
'<span><span style="color:#67c23a">Android: </span>{{ platforms.android }}</span></br>' +
|
||||
'<span>{{ $t("dialog.user.info.instance_game_version") }} {{ gameServerVersion }}</span></br>' +
|
||||
'<span v-if="queueEnabled">{{ $t("dialog.user.info.instance_queuing_enabled") }}</br></span>' +
|
||||
'<span v-if="disabledContentSettings">{{ $t("dialog.user.info.instance_disabled_content") }} {{ disabledContentSettings }}</br></span>' +
|
||||
'<span v-if="userList.length">{{ $t("dialog.user.info.instance_users") }}</br></span>' +
|
||||
'<template v-for="user in userList"><span style="cursor:pointer;margin-right:5px" @click="showUserDialog(user.id)" v-text="user.displayName"></span></template>' +
|
||||
'</div>' +
|
||||
'<i class="el-icon-caret-bottom"></i>' +
|
||||
'</el-tooltip>' +
|
||||
'<span v-if="occupants" style="margin-left:5px">{{ occupants }}/{{ capacity }}</span>' +
|
||||
'<span v-if="friendcount" style="margin-left:5px">({{ friendcount }})</span>' +
|
||||
'<span v-if="isFull" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_full") }}</span>' +
|
||||
'<span v-if="isHardClosed" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_hard_closed") }}</span>' +
|
||||
'<span v-else-if="isClosed" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_closed") }}</span>' +
|
||||
'<span v-if="queueSize" style="margin-left:5px">{{ $t("dialog.user.info.instance_queue") }} {{ queueSize }}</span>' +
|
||||
'<span v-if="isAgeGated" style="margin-left:5px;color:lightcoral">{{ $t("dialog.user.info.instance_age_gated") }}</span>' +
|
||||
'</div>',
|
||||
props: {
|
||||
location: String,
|
||||
instance: Object,
|
||||
friendcount: Number,
|
||||
updateelement: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isValidInstance: this.isValidInstance,
|
||||
isFull: this.isFull,
|
||||
isClosed: this.isClosed,
|
||||
isHardClosed: this.isHardClosed,
|
||||
closedAt: this.closedAt,
|
||||
occupants: this.occupants,
|
||||
capacity: this.capacity,
|
||||
queueSize: this.queueSize,
|
||||
queueEnabled: this.queueEnabled,
|
||||
platforms: this.platforms,
|
||||
userList: this.userList,
|
||||
gameServerVersion: this.gameServerVersion,
|
||||
canCloseInstance: this.canCloseInstance
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
this.isValidInstance = false;
|
||||
this.isFull = false;
|
||||
this.isClosed = false;
|
||||
this.isHardClosed = false;
|
||||
this.closedAt = '';
|
||||
this.occupants = 0;
|
||||
this.capacity = 0;
|
||||
this.queueSize = 0;
|
||||
this.queueEnabled = false;
|
||||
this.platforms = [];
|
||||
this.userList = [];
|
||||
this.gameServerVersion = '';
|
||||
this.canCloseInstance = false;
|
||||
this.isAgeGated = false;
|
||||
this.disabledContentSettings = '';
|
||||
if (
|
||||
!this.location ||
|
||||
!this.instance ||
|
||||
Object.keys(this.instance).length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.isValidInstance = true;
|
||||
this.isFull =
|
||||
typeof this.instance.hasCapacityForYou !==
|
||||
'undefined' && !this.instance.hasCapacityForYou;
|
||||
if (this.instance.closedAt) {
|
||||
this.isClosed = true;
|
||||
this.closedAt = this.instance.closedAt;
|
||||
}
|
||||
this.isHardClosed = this.instance.hardClose === true;
|
||||
this.occupants = this.instance.userCount;
|
||||
if (this.location === $app.lastLocation.location) {
|
||||
// use gameLog for occupants when in same location
|
||||
this.occupants = $app.lastLocation.playerList.size;
|
||||
}
|
||||
this.capacity = this.instance.capacity;
|
||||
this.gameServerVersion = this.instance.gameServerVersion;
|
||||
this.queueSize = this.instance.queueSize;
|
||||
if (this.instance.platforms) {
|
||||
this.platforms = this.instance.platforms;
|
||||
}
|
||||
if (this.instance.users) {
|
||||
this.userList = this.instance.users;
|
||||
}
|
||||
if (this.instance.ownerId === API.currentUser.id) {
|
||||
this.canCloseInstance = true;
|
||||
} else if (this.instance?.ownerId?.startsWith('grp_')) {
|
||||
// check group perms
|
||||
var groupId = this.instance.ownerId;
|
||||
var group = API.cachedGroups.get(groupId);
|
||||
this.canCloseInstance = hasGroupPermission(
|
||||
group,
|
||||
'group-instance-moderate'
|
||||
);
|
||||
}
|
||||
this.isAgeGated = this.instance.ageGate === true;
|
||||
if (this.location && this.location.includes('~ageGate')) {
|
||||
// dumb workaround for API not returning `ageGate`
|
||||
this.isAgeGated = true;
|
||||
}
|
||||
if (
|
||||
this.instance.$disabledContentSettings &&
|
||||
this.instance.$disabledContentSettings.length
|
||||
) {
|
||||
this.disabledContentSettings =
|
||||
this.instance.$disabledContentSettings.join(', ');
|
||||
}
|
||||
},
|
||||
showUserDialog(userId) {
|
||||
this.showUserDialog(userId);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
updateelement() {
|
||||
this.parse();
|
||||
},
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
friendcount() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('avatar-info', {
|
||||
template:
|
||||
'<div @click="confirm" class="avatar-info">' +
|
||||
'<span style="margin-right:5px">{{ avatarName }}</span>' +
|
||||
'<span v-if="avatarType" style="margin-right:5px" :class="color">{{ avatarType }}</span>' +
|
||||
'<span v-if="avatarTags" style="color:#909399;font-family:monospace;font-size:12px;">{{ avatarTags }}</span>' +
|
||||
'</div>',
|
||||
props: {
|
||||
imageurl: String,
|
||||
userid: String,
|
||||
hintownerid: String,
|
||||
hintavatarname: String,
|
||||
avatartags: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
avatarName: this.avatarName,
|
||||
avatarType: this.avatarType,
|
||||
avatarTags: this.avatarTags,
|
||||
color: this.color
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async parse() {
|
||||
this.ownerId = '';
|
||||
this.avatarName = '';
|
||||
this.avatarType = '';
|
||||
this.color = '';
|
||||
this.avatarTags = '';
|
||||
if (!this.imageurl) {
|
||||
this.avatarName = '-';
|
||||
} else if (this.hintownerid) {
|
||||
this.avatarName = this.hintavatarname;
|
||||
this.ownerId = this.hintownerid;
|
||||
} else {
|
||||
try {
|
||||
var avatarInfo = await $app.getAvatarName(
|
||||
this.imageurl
|
||||
);
|
||||
this.avatarName = avatarInfo.avatarName;
|
||||
this.ownerId = avatarInfo.ownerId;
|
||||
} catch (err) {}
|
||||
}
|
||||
if (typeof this.userid === 'undefined' || !this.ownerId) {
|
||||
this.color = '';
|
||||
this.avatarType = '';
|
||||
} else if (this.ownerId === this.userid) {
|
||||
this.color = 'avatar-info-own';
|
||||
this.avatarType = '(own)';
|
||||
} else {
|
||||
this.color = 'avatar-info-public';
|
||||
this.avatarType = '(public)';
|
||||
}
|
||||
if (typeof this.avatartags === 'object') {
|
||||
var tagString = '';
|
||||
for (var i = 0; i < this.avatartags.length; i++) {
|
||||
var tagName = this.avatartags[i].replace(
|
||||
'content_',
|
||||
''
|
||||
);
|
||||
tagString += tagName;
|
||||
if (i < this.avatartags.length - 1) {
|
||||
tagString += ', ';
|
||||
}
|
||||
}
|
||||
this.avatarTags = tagString;
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
if (!this.imageurl) {
|
||||
return;
|
||||
}
|
||||
$app.showAvatarAuthorDialog(
|
||||
this.userid,
|
||||
this.ownerId,
|
||||
this.imageurl
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
imageurl() {
|
||||
this.parse();
|
||||
},
|
||||
userid() {
|
||||
this.parse();
|
||||
},
|
||||
avatartags() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('display-name', {
|
||||
template:
|
||||
'<span @click="showUserDialog" class="x-link">{{ username }}</span>',
|
||||
props: {
|
||||
userid: String,
|
||||
location: String,
|
||||
forceUpdateKey: Number,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: this.username
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async parse() {
|
||||
this.username = this.userid;
|
||||
if (this.hint) {
|
||||
this.username = this.hint;
|
||||
} else if (this.userid) {
|
||||
var args = await userRequest.getCachedUser({
|
||||
userId: this.userid
|
||||
});
|
||||
}
|
||||
if (
|
||||
typeof args !== 'undefined' &&
|
||||
typeof args.json !== 'undefined' &&
|
||||
typeof args.json.displayName !== 'undefined'
|
||||
) {
|
||||
this.username = args.json.displayName;
|
||||
}
|
||||
},
|
||||
showUserDialog() {
|
||||
$app.showUserDialog(this.userid);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
},
|
||||
forceUpdateKey() {
|
||||
this.parse();
|
||||
},
|
||||
userid() {
|
||||
this.parse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.parse();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { baseClass, $app, API } from './baseClass.js';
|
||||
import { groupRequest } from '../api/index.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.$on('LOGIN', function () {
|
||||
$app.nextCurrentUserRefresh = 300;
|
||||
$app.nextFriendsRefresh = 3600;
|
||||
$app.nextGroupInstanceRefresh = 0;
|
||||
});
|
||||
}
|
||||
|
||||
_data = {
|
||||
nextCurrentUserRefresh: 300,
|
||||
nextFriendsRefresh: 3600,
|
||||
nextGroupInstanceRefresh: 0,
|
||||
nextAppUpdateCheck: 3600,
|
||||
ipcTimeout: 0,
|
||||
nextClearVRCXCacheCheck: 0,
|
||||
nextDiscordUpdate: 0,
|
||||
nextAutoStateChange: 0,
|
||||
nextGetLogCheck: 0,
|
||||
nextGameRunningCheck: 0,
|
||||
nextDatabaseOptimize: 3600
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async updateLoop() {
|
||||
try {
|
||||
if (API.isLoggedIn === true) {
|
||||
if (--this.nextCurrentUserRefresh <= 0) {
|
||||
this.nextCurrentUserRefresh = 300; // 5min
|
||||
API.getCurrentUser();
|
||||
}
|
||||
if (--this.nextFriendsRefresh <= 0) {
|
||||
this.nextFriendsRefresh = 3600; // 1hour
|
||||
this.refreshFriendsList();
|
||||
this.updateStoredUser(API.currentUser);
|
||||
if (this.isGameRunning) {
|
||||
API.refreshPlayerModerations();
|
||||
}
|
||||
}
|
||||
if (--this.nextGroupInstanceRefresh <= 0) {
|
||||
if (this.friendLogInitStatus) {
|
||||
this.nextGroupInstanceRefresh = 300; // 5min
|
||||
groupRequest.getUsersGroupInstances();
|
||||
}
|
||||
AppApi.CheckGameRunning();
|
||||
}
|
||||
if (--this.nextAppUpdateCheck <= 0) {
|
||||
this.nextAppUpdateCheck = 3600; // 1hour
|
||||
if (this.autoUpdateVRCX !== 'Off') {
|
||||
this.checkForVRCXUpdate();
|
||||
}
|
||||
}
|
||||
if (--this.ipcTimeout <= 0) {
|
||||
this.ipcEnabled = false;
|
||||
}
|
||||
if (
|
||||
--this.nextClearVRCXCacheCheck <= 0 &&
|
||||
this.clearVRCXCacheFrequency > 0
|
||||
) {
|
||||
this.nextClearVRCXCacheCheck =
|
||||
this.clearVRCXCacheFrequency / 2;
|
||||
this.clearVRCXCache();
|
||||
}
|
||||
if (--this.nextDiscordUpdate <= 0) {
|
||||
this.nextDiscordUpdate = 3;
|
||||
if (this.discordActive) {
|
||||
this.updateDiscord();
|
||||
}
|
||||
}
|
||||
if (--this.nextAutoStateChange <= 0) {
|
||||
this.nextAutoStateChange = 3;
|
||||
this.updateAutoStateChange();
|
||||
}
|
||||
if (
|
||||
(this.isRunningUnderWine || LINUX) &&
|
||||
--this.nextGetLogCheck <= 0
|
||||
) {
|
||||
this.nextGetLogCheck = 0.5;
|
||||
const logLines = await LogWatcher.GetLogLines();
|
||||
if (logLines) {
|
||||
logLines.forEach((logLine) => {
|
||||
$app.addGameLogEvent(logLine);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
(this.isRunningUnderWine || LINUX) &&
|
||||
--this.nextGameRunningCheck <= 0
|
||||
) {
|
||||
if (LINUX) {
|
||||
this.nextGameRunningCheck = 1;
|
||||
$app.updateIsGameRunning(
|
||||
await AppApi.IsGameRunning(),
|
||||
await AppApi.IsSteamVRRunning(),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
this.nextGameRunningCheck = 3;
|
||||
AppApi.CheckGameRunning();
|
||||
}
|
||||
}
|
||||
if (--this.nextDatabaseOptimize <= 0) {
|
||||
this.nextDatabaseOptimize = 86400; // 1 day
|
||||
database.optimize();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
API.isRefreshFriendsLoading = false;
|
||||
console.error(err);
|
||||
}
|
||||
workerTimers.setTimeout(() => this.updateLoop(), 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import { userRequest } from '../api';
|
||||
import database from '../service/database.js';
|
||||
import utils from '../classes/utils';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
const userNotes = {
|
||||
lastNoteCheck: null,
|
||||
lastDbNoteDate: null,
|
||||
notes: new Map(),
|
||||
|
||||
async init() {
|
||||
this.lastNoteCheck = new Date();
|
||||
this.lastDbNoteDate = null;
|
||||
this.notes.clear();
|
||||
try {
|
||||
// todo: get users from store
|
||||
const users = window.API.cachedUsers;
|
||||
const dbNotes = await database.getAllUserNotes();
|
||||
for (const note of dbNotes) {
|
||||
this.notes.set(note.userId, note.note);
|
||||
const user = users.get(note.userId);
|
||||
if (user) {
|
||||
user.note = note.note;
|
||||
}
|
||||
if (
|
||||
!this.lastDbNoteDate ||
|
||||
this.lastDbNoteDate < note.createdAt
|
||||
) {
|
||||
this.lastDbNoteDate = note.createdAt;
|
||||
}
|
||||
}
|
||||
await this.getLatestUserNotes();
|
||||
} catch (error) {
|
||||
console.error('Error initializing user notes:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async getLatestUserNotes() {
|
||||
this.lastNoteCheck = new Date();
|
||||
const params = {
|
||||
offset: 0,
|
||||
n: 10 // start light
|
||||
};
|
||||
const newNotes = new Map();
|
||||
let done = false;
|
||||
try {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
params.offset = i * params.n;
|
||||
const args = await userRequest.getUserNotes(params);
|
||||
for (const note of args.json) {
|
||||
if (
|
||||
this.lastDbNoteDate &&
|
||||
this.lastDbNoteDate > note.createdAt
|
||||
) {
|
||||
done = true;
|
||||
}
|
||||
if (
|
||||
!this.lastDbNoteDate ||
|
||||
this.lastDbNoteDate < note.createdAt
|
||||
) {
|
||||
this.lastDbNoteDate = note.createdAt;
|
||||
}
|
||||
note.note = utils.replaceBioSymbols(note.note);
|
||||
newNotes.set(note.targetUserId, note);
|
||||
}
|
||||
if (done || args.json.length === 0) {
|
||||
break;
|
||||
}
|
||||
params.n = 100; // crank it after first run
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 1000);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching user notes:', error);
|
||||
}
|
||||
// todo: get users from store
|
||||
const users = window.API.cachedUsers;
|
||||
|
||||
for (const note of newNotes.values()) {
|
||||
const newNote = {
|
||||
userId: note.targetUserId,
|
||||
displayName: note.targetUser?.displayName || note.targetUserId,
|
||||
note: note.note,
|
||||
createdAt: note.createdAt
|
||||
};
|
||||
await database.addUserNote(newNote);
|
||||
this.notes.set(note.targetUserId, note.note);
|
||||
const user = users.get(note.targetUserId);
|
||||
if (user) {
|
||||
user.note = note.note;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async checkNote(userId, newNote) {
|
||||
// last check was more than than 5 minutes ago
|
||||
if (
|
||||
!this.lastNoteCheck ||
|
||||
this.lastNoteCheck.getTime() + 5 * 60 * 1000 > Date.now()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const existingNote = this.notes.get(userId);
|
||||
if (typeof existingNote !== 'undefined' && !newNote) {
|
||||
console.log('deleting note', userId);
|
||||
this.notes.delete(userId);
|
||||
await database.deleteUserNote(userId);
|
||||
return;
|
||||
}
|
||||
if (typeof existingNote === 'undefined' || existingNote !== newNote) {
|
||||
console.log('detected note change', userId, newNote);
|
||||
await this.getLatestUserNotes();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { userNotes };
|
||||
@@ -1,302 +0,0 @@
|
||||
let echarts = null;
|
||||
|
||||
const _utils = {
|
||||
removeFromArray(array, item) {
|
||||
var { length } = array;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (array[i] === item) {
|
||||
array.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
arraysMatch(a, b) {
|
||||
if (!Array.isArray(a) || !Array.isArray(b)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
a.length === b.length &&
|
||||
a.every(
|
||||
(element, index) =>
|
||||
JSON.stringify(element) === JSON.stringify(b[index])
|
||||
)
|
||||
);
|
||||
},
|
||||
moveArrayItem(array, fromIndex, toIndex) {
|
||||
if (!Array.isArray(array) || fromIndex === toIndex) {
|
||||
return;
|
||||
}
|
||||
if (fromIndex < 0 || fromIndex >= array.length) {
|
||||
return;
|
||||
}
|
||||
if (toIndex < 0 || toIndex >= array.length) {
|
||||
return;
|
||||
}
|
||||
const item = array[fromIndex];
|
||||
array.splice(fromIndex, 1);
|
||||
array.splice(toIndex, 0, item);
|
||||
},
|
||||
escapeTag(tag) {
|
||||
var s = String(tag);
|
||||
return s.replace(/["&'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
||||
},
|
||||
escapeTagRecursive(obj) {
|
||||
if (typeof obj === 'string') {
|
||||
return this.escapeTag(obj);
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
for (var key in obj) {
|
||||
obj[key] = this.escapeTagRecursive(obj[key]);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
timeToText(sec, isNeedSeconds = false) {
|
||||
let n = Number(sec);
|
||||
if (isNaN(n)) {
|
||||
return this.escapeTag(sec);
|
||||
}
|
||||
n = Math.floor(n / 1000);
|
||||
const arr = [];
|
||||
if (n < 0) {
|
||||
n = -n;
|
||||
}
|
||||
if (n >= 86400) {
|
||||
arr.push(`${Math.floor(n / 86400)}d`);
|
||||
n %= 86400;
|
||||
}
|
||||
if (n >= 3600) {
|
||||
arr.push(`${Math.floor(n / 3600)}h`);
|
||||
n %= 3600;
|
||||
}
|
||||
if (n >= 60) {
|
||||
arr.push(`${Math.floor(n / 60)}m`);
|
||||
n %= 60;
|
||||
}
|
||||
if (isNeedSeconds || (arr.length === 0 && n < 60)) {
|
||||
arr.push(`${n}s`);
|
||||
}
|
||||
return arr.join(' ');
|
||||
},
|
||||
textToHex(text) {
|
||||
var s = String(text);
|
||||
return s
|
||||
.split('')
|
||||
.map((c) => c.charCodeAt(0).toString(16))
|
||||
.join(' ');
|
||||
},
|
||||
commaNumber(num) {
|
||||
if (!num) {
|
||||
return '0';
|
||||
}
|
||||
var s = String(Number(num));
|
||||
return s.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||
},
|
||||
buildTreeData(json) {
|
||||
var node = [];
|
||||
for (var key in json) {
|
||||
if (key[0] === '$') {
|
||||
continue;
|
||||
}
|
||||
var value = json[key];
|
||||
if (Array.isArray(value) && value.length === 0) {
|
||||
node.push({
|
||||
key,
|
||||
value: '[]'
|
||||
});
|
||||
} else if (
|
||||
value === Object(value) &&
|
||||
Object.keys(value).length === 0
|
||||
) {
|
||||
node.push({
|
||||
key,
|
||||
value: '{}'
|
||||
});
|
||||
} else if (Array.isArray(value)) {
|
||||
node.push({
|
||||
children: value.map((val, idx) => {
|
||||
if (val === Object(val)) {
|
||||
return {
|
||||
children: this.buildTreeData(val),
|
||||
key: idx
|
||||
};
|
||||
}
|
||||
return {
|
||||
key: idx,
|
||||
value: val
|
||||
};
|
||||
}),
|
||||
key
|
||||
});
|
||||
} else if (value === Object(value)) {
|
||||
node.push({
|
||||
children: this.buildTreeData(value),
|
||||
key
|
||||
});
|
||||
} else {
|
||||
node.push({
|
||||
key,
|
||||
value: String(value)
|
||||
});
|
||||
}
|
||||
}
|
||||
node.sort(function (a, b) {
|
||||
var A = String(a.key).toUpperCase();
|
||||
var B = String(b.key).toUpperCase();
|
||||
if (A < B) {
|
||||
return -1;
|
||||
}
|
||||
if (A > B) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return node;
|
||||
},
|
||||
// descending
|
||||
compareByCreatedAt(a, b) {
|
||||
if (
|
||||
typeof a.created_at !== 'string' ||
|
||||
typeof b.created_at !== 'string'
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
var A = a.created_at.toUpperCase();
|
||||
var B = b.created_at.toUpperCase();
|
||||
if (A < B) {
|
||||
return 1;
|
||||
}
|
||||
if (A > B) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
// lazy load echarts
|
||||
loadEcharts() {
|
||||
if (echarts) {
|
||||
return Promise.resolve(echarts);
|
||||
}
|
||||
return import('echarts').then((module) => {
|
||||
echarts = module;
|
||||
return echarts;
|
||||
});
|
||||
},
|
||||
// CJK character in Japanese, Korean, Chinese are different
|
||||
// so change font-family order when users change language to display CJK character correctly
|
||||
changeCJKorder(lang) {
|
||||
const otherFonts = window
|
||||
.getComputedStyle(document.body)
|
||||
.fontFamily.split(',')
|
||||
.filter((item) => !item.includes('Noto Sans'))
|
||||
.join(', ');
|
||||
const notoSans = 'Noto Sans';
|
||||
|
||||
const fontFamilies = {
|
||||
ja_JP: ['JP', 'KR', 'TC', 'SC'],
|
||||
ko: ['KR', 'JP', 'TC', 'SC'],
|
||||
zh_TW: ['TC', 'JP', 'KR', 'SC'],
|
||||
zh_CN: ['SC', 'JP', 'KR', 'TC']
|
||||
};
|
||||
|
||||
if (fontFamilies[lang]) {
|
||||
const CJKFamily = fontFamilies[lang]
|
||||
.map((item) => `${notoSans} ${item}`)
|
||||
.join(', ');
|
||||
document.body.style.fontFamily = `${CJKFamily}, ${otherFonts}`;
|
||||
}
|
||||
},
|
||||
localeIncludes(str, search, comparer) {
|
||||
// These checks are stolen from https://stackoverflow.com/a/69623589/11030436
|
||||
if (search === '') {
|
||||
return true;
|
||||
} else if (!str || !search) {
|
||||
return false;
|
||||
}
|
||||
const strObj = String(str);
|
||||
const searchObj = String(search);
|
||||
|
||||
if (strObj.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (searchObj.length > strObj.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now simply loop through each substring and compare them
|
||||
for (let i = 0; i < str.length - searchObj.length + 1; i++) {
|
||||
const substr = strObj.substring(i, i + searchObj.length);
|
||||
if (comparer.compare(substr, searchObj) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
compareByName(a, b) {
|
||||
if (typeof a.name !== 'string' || typeof b.name !== 'string') {
|
||||
return 0;
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
},
|
||||
replaceBioSymbols(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
var symbolList = {
|
||||
'@': '@',
|
||||
'#': '#',
|
||||
$: '$',
|
||||
'%': '%',
|
||||
'&': '&',
|
||||
'=': '=',
|
||||
'+': '+',
|
||||
'/': '⁄',
|
||||
'\\': '\',
|
||||
';': ';',
|
||||
':': '˸',
|
||||
',': '‚',
|
||||
'?': '?',
|
||||
'!': 'ǃ',
|
||||
'"': '"',
|
||||
'<': '≺',
|
||||
'>': '≻',
|
||||
'.': '․',
|
||||
'^': '^',
|
||||
'{': '{',
|
||||
'}': '}',
|
||||
'[': '[',
|
||||
']': ']',
|
||||
'(': '(',
|
||||
')': ')',
|
||||
'|': '|',
|
||||
'*': '∗'
|
||||
};
|
||||
var newText = text;
|
||||
for (var key in symbolList) {
|
||||
var regex = new RegExp(symbolList[key], 'g');
|
||||
newText = newText.replace(regex, key);
|
||||
}
|
||||
return newText.replace(/ {1,}/g, ' ').trimRight();
|
||||
},
|
||||
// descending
|
||||
compareByUpdatedAt(a, b) {
|
||||
if (
|
||||
typeof a.updated_at !== 'string' ||
|
||||
typeof b.updated_at !== 'string'
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
var A = a.updated_at.toUpperCase();
|
||||
var B = b.updated_at.toUpperCase();
|
||||
if (A < B) {
|
||||
return 1;
|
||||
}
|
||||
if (A > B) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
export default _utils;
|
||||
@@ -1,121 +0,0 @@
|
||||
import configRepository from '../service/config.js';
|
||||
import { baseClass, $t, $utils } from './baseClass.js';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {
|
||||
async backupVrcRegistry(name) {
|
||||
var regJson;
|
||||
if (LINUX) {
|
||||
regJson = await AppApi.GetVRChatRegistryJson();
|
||||
regJson = JSON.parse(regJson);
|
||||
} else {
|
||||
regJson = await AppApi.GetVRChatRegistry();
|
||||
}
|
||||
var newBackup = {
|
||||
name,
|
||||
date: new Date().toJSON(),
|
||||
data: regJson
|
||||
};
|
||||
var backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
var backups = JSON.parse(backupsJson);
|
||||
backups.push(newBackup);
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
// await this.updateRegistryBackupDialog();
|
||||
},
|
||||
|
||||
// Because it is a startup func, it is not integrated into RegistryBackupDialog.vue now
|
||||
// func backupVrcRegistry is also split up
|
||||
async checkAutoBackupRestoreVrcRegistry() {
|
||||
if (!this.vrcRegistryAutoBackup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for auto restore
|
||||
var hasVRChatRegistryFolder =
|
||||
await AppApi.HasVRChatRegistryFolder();
|
||||
if (!hasVRChatRegistryFolder) {
|
||||
var lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
var lastRestoreCheck = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck'
|
||||
);
|
||||
if (
|
||||
!lastBackupDate ||
|
||||
(lastRestoreCheck &&
|
||||
lastBackupDate &&
|
||||
lastRestoreCheck === lastBackupDate)
|
||||
) {
|
||||
// only ask to restore once and when backup is present
|
||||
return;
|
||||
}
|
||||
// popup message about auto restore
|
||||
this.$alert(
|
||||
$t('dialog.registry_backup.restore_prompt'),
|
||||
$t('dialog.registry_backup.header')
|
||||
);
|
||||
this.showRegistryBackupDialog();
|
||||
await AppApi.FocusWindow();
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastRestoreCheck',
|
||||
lastBackupDate
|
||||
);
|
||||
} else {
|
||||
await this.autoBackupVrcRegistry();
|
||||
}
|
||||
},
|
||||
|
||||
async autoBackupVrcRegistry() {
|
||||
var date = new Date();
|
||||
var lastBackupDate = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryLastBackupDate'
|
||||
);
|
||||
if (lastBackupDate) {
|
||||
var lastBackup = new Date(lastBackupDate);
|
||||
var diff = date.getTime() - lastBackup.getTime();
|
||||
var diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
if (diffDays < 7) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var backupsJson = await configRepository.getString(
|
||||
'VRCX_VRChatRegistryBackups'
|
||||
);
|
||||
if (!backupsJson) {
|
||||
backupsJson = JSON.stringify([]);
|
||||
}
|
||||
var backups = JSON.parse(backupsJson);
|
||||
backups.forEach((backup) => {
|
||||
if (backup.name === 'Auto Backup') {
|
||||
// remove old auto backup
|
||||
$utils.removeFromArray(backups, backup);
|
||||
}
|
||||
});
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryBackups',
|
||||
JSON.stringify(backups)
|
||||
);
|
||||
this.backupVrcRegistry('Auto Backup');
|
||||
await configRepository.setString(
|
||||
'VRCX_VRChatRegistryLastBackupDate',
|
||||
date.toJSON()
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,351 +0,0 @@
|
||||
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
_data = {
|
||||
VRCXUpdateDialog: {
|
||||
visible: false,
|
||||
updatePending: false,
|
||||
updatePendingIsLatest: false,
|
||||
release: '',
|
||||
releases: [],
|
||||
json: {}
|
||||
},
|
||||
branch: 'Stable',
|
||||
autoUpdateVRCX: 'Auto Download',
|
||||
checkingForVRCXUpdate: false,
|
||||
pendingVRCXInstall: '',
|
||||
pendingVRCXUpdate: false,
|
||||
branches: {
|
||||
Stable: {
|
||||
name: 'Stable',
|
||||
urlReleases: 'https://api0.vrcx.app/releases/stable',
|
||||
urlLatest: 'https://api0.vrcx.app/releases/stable/latest'
|
||||
},
|
||||
Nightly: {
|
||||
name: 'Nightly',
|
||||
urlReleases: 'https://api0.vrcx.app/releases/nightly',
|
||||
urlLatest: 'https://api0.vrcx.app/releases/nightly/latest'
|
||||
}
|
||||
// LinuxTest: {
|
||||
// name: 'LinuxTest',
|
||||
// urlReleases: 'https://api.github.com/repos/rs189/VRCX/releases',
|
||||
// urlLatest:
|
||||
// 'https://api.github.com/repos/rs189/VRCX/releases/latest'
|
||||
// }
|
||||
},
|
||||
updateProgress: 0,
|
||||
updateInProgress: false
|
||||
};
|
||||
|
||||
_methods = {
|
||||
async showVRCXUpdateDialog() {
|
||||
var D = this.VRCXUpdateDialog;
|
||||
D.visible = true;
|
||||
D.updatePendingIsLatest = false;
|
||||
D.updatePending = await AppApi.CheckForUpdateExe();
|
||||
this.loadBranchVersions();
|
||||
},
|
||||
|
||||
async downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
) {
|
||||
if (this.updateInProgress) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.updateInProgress = true;
|
||||
this.downloadFileProgress();
|
||||
await AppApi.DownloadUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size
|
||||
);
|
||||
this.pendingVRCXInstall = releaseName;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message({
|
||||
message: `${$t('message.vrcx_updater.failed_install')} ${err}`,
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
this.updateInProgress = false;
|
||||
this.updateProgress = 0;
|
||||
}
|
||||
},
|
||||
|
||||
async cancelUpdate() {
|
||||
await AppApi.CancelUpdate();
|
||||
this.updateInProgress = false;
|
||||
this.updateProgress = 0;
|
||||
},
|
||||
|
||||
async downloadFileProgress() {
|
||||
this.updateProgress = await AppApi.CheckUpdateProgress();
|
||||
if (this.updateInProgress) {
|
||||
workerTimers.setTimeout(() => this.downloadFileProgress(), 150);
|
||||
}
|
||||
},
|
||||
|
||||
updateProgressText() {
|
||||
if (this.updateProgress === 100) {
|
||||
return $t('message.vrcx_updater.checking_hash');
|
||||
}
|
||||
return `${this.updateProgress}%`;
|
||||
},
|
||||
|
||||
installVRCXUpdate() {
|
||||
for (var release of this.VRCXUpdateDialog.releases) {
|
||||
if (release.name !== this.VRCXUpdateDialog.release) {
|
||||
continue;
|
||||
}
|
||||
var downloadUrl = '';
|
||||
var downloadName = '';
|
||||
var hashUrl = '';
|
||||
var size = 0;
|
||||
for (var asset of release.assets) {
|
||||
if (asset.state !== 'uploaded') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
WINDOWS &&
|
||||
(asset.content_type === 'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program')
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
LINUX &&
|
||||
asset.content_type === 'application/octet-stream'
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
asset.name === 'SHA256SUMS.txt' &&
|
||||
asset.content_type === 'text/plain'
|
||||
) {
|
||||
hashUrl = asset.browser_download_url;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!downloadUrl) {
|
||||
return;
|
||||
}
|
||||
var releaseName = release.name;
|
||||
var type = 'Manual';
|
||||
this.downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
async loadBranchVersions() {
|
||||
var D = this.VRCXUpdateDialog;
|
||||
var url = this.branches[this.branch].urlReleases;
|
||||
this.checkingForVRCXUpdate = true;
|
||||
try {
|
||||
var response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'VRCX-ID': this.vrcxId
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
this.checkingForVRCXUpdate = false;
|
||||
}
|
||||
var json = JSON.parse(response.data);
|
||||
if (this.debugWebRequests) {
|
||||
console.log(json, response);
|
||||
}
|
||||
var releases = [];
|
||||
if (typeof json !== 'object' || json.message) {
|
||||
$app.$message({
|
||||
message: $t('message.vrcx_updater.failed', {
|
||||
message: json.message
|
||||
}),
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
for (var release of json) {
|
||||
for (var asset of release.assets) {
|
||||
if (
|
||||
(asset.content_type === 'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program') &&
|
||||
asset.state === 'uploaded'
|
||||
) {
|
||||
releases.push(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
D.releases = releases;
|
||||
D.release = json[0].name;
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = false;
|
||||
if (D.release === this.pendingVRCXInstall) {
|
||||
// update already downloaded and latest version
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = true;
|
||||
}
|
||||
if (
|
||||
(await configRepository.getString('VRCX_branch')) !==
|
||||
this.branch
|
||||
) {
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
},
|
||||
|
||||
async checkForVRCXUpdate() {
|
||||
var currentVersion = this.appVersion.replace(' (Linux)', '');
|
||||
if (
|
||||
!currentVersion ||
|
||||
currentVersion === 'VRCX Nightly Build' ||
|
||||
currentVersion === 'VRCX Build'
|
||||
) {
|
||||
// ignore custom builds
|
||||
return;
|
||||
}
|
||||
if (this.branch === 'Beta') {
|
||||
// move Beta users to stable
|
||||
this.branch = 'Stable';
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
if (typeof this.branches[this.branch] === 'undefined') {
|
||||
// handle invalid branch
|
||||
this.branch = 'Stable';
|
||||
await configRepository.setString('VRCX_branch', this.branch);
|
||||
}
|
||||
var url = this.branches[this.branch].urlLatest;
|
||||
this.checkingForVRCXUpdate = true;
|
||||
try {
|
||||
var response = await webApiService.execute({
|
||||
url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'VRCX-ID': this.vrcxId
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
this.checkingForVRCXUpdate = false;
|
||||
}
|
||||
this.pendingVRCXUpdate = false;
|
||||
var json = JSON.parse(response.data);
|
||||
if (this.debugWebRequests) {
|
||||
console.log(json, response);
|
||||
}
|
||||
if (json === Object(json) && json.name && json.published_at) {
|
||||
this.VRCXUpdateDialog.updateJson = json;
|
||||
this.changeLogDialog.buildName = json.name;
|
||||
this.changeLogDialog.changeLog = this.changeLogRemoveLinks(
|
||||
json.body
|
||||
);
|
||||
var releaseName = json.name;
|
||||
this.latestAppVersion = releaseName;
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = false;
|
||||
if (releaseName === this.pendingVRCXInstall) {
|
||||
// update already downloaded
|
||||
this.VRCXUpdateDialog.updatePendingIsLatest = true;
|
||||
} else if (releaseName > currentVersion) {
|
||||
var downloadUrl = '';
|
||||
var downloadName = '';
|
||||
var hashUrl = '';
|
||||
var size = 0;
|
||||
for (var asset of json.assets) {
|
||||
if (asset.state !== 'uploaded') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!LINUX &&
|
||||
(asset.content_type ===
|
||||
'application/x-msdownload' ||
|
||||
asset.content_type ===
|
||||
'application/x-msdos-program')
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
LINUX &&
|
||||
asset.content_type === 'application/octet-stream'
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
downloadName = asset.name;
|
||||
size = asset.size;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
asset.name === 'SHA256SUMS.txt' &&
|
||||
asset.content_type === 'text/plain'
|
||||
) {
|
||||
hashUrl = asset.browser_download_url;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!downloadUrl) {
|
||||
return;
|
||||
}
|
||||
this.pendingVRCXUpdate = true;
|
||||
this.notifyMenu('settings');
|
||||
var type = 'Auto';
|
||||
if (!API.isLoggedIn) {
|
||||
this.showVRCXUpdateDialog();
|
||||
} else if (this.autoUpdateVRCX === 'Notify') {
|
||||
// this.showVRCXUpdateDialog();
|
||||
} else if (this.autoUpdateVRCX === 'Auto Download') {
|
||||
this.downloadVRCXUpdate(
|
||||
downloadUrl,
|
||||
downloadName,
|
||||
hashUrl,
|
||||
size,
|
||||
releaseName,
|
||||
type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
restartVRCX(isUpgrade) {
|
||||
if (!LINUX) {
|
||||
AppApi.RestartApplication(isUpgrade);
|
||||
} else {
|
||||
window.electron.restartApp();
|
||||
}
|
||||
},
|
||||
|
||||
async saveAutoUpdateVRCX() {
|
||||
if (this.autoUpdateVRCX === 'Off') {
|
||||
this.pendingVRCXUpdate = false;
|
||||
}
|
||||
await configRepository.setString(
|
||||
'VRCX_autoUpdateVRCX',
|
||||
this.autoUpdateVRCX
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,620 +0,0 @@
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import Noty from 'noty';
|
||||
import { parseLocation } from '../composables/instance/utils';
|
||||
import { baseClass, $app, API, $utils } from './baseClass.js';
|
||||
import { groupRequest } from '../api';
|
||||
|
||||
export default class extends baseClass {
|
||||
constructor(_app, _API, _t) {
|
||||
super(_app, _API, _t);
|
||||
}
|
||||
|
||||
init() {
|
||||
API.webSocket = null;
|
||||
API.lastWebSocketMessage = '';
|
||||
|
||||
API.$on('USER:CURRENT', function () {
|
||||
if ($app.friendLogInitStatus && this.webSocket === null) {
|
||||
this.getAuth();
|
||||
}
|
||||
});
|
||||
|
||||
API.getAuth = function () {
|
||||
return this.call('auth', {
|
||||
method: 'GET'
|
||||
}).then((json) => {
|
||||
var args = {
|
||||
json
|
||||
};
|
||||
this.$emit('AUTH', args);
|
||||
return args;
|
||||
});
|
||||
};
|
||||
|
||||
API.$on('AUTH', function (args) {
|
||||
if (args.json.ok) {
|
||||
this.connectWebSocket(args.json.token);
|
||||
}
|
||||
});
|
||||
|
||||
API.connectWebSocket = function (token) {
|
||||
if (this.webSocket !== null) {
|
||||
return;
|
||||
}
|
||||
var socket = new WebSocket(`${API.websocketDomain}/?auth=${token}`);
|
||||
socket.onopen = () => {
|
||||
if ($app.debugWebSocket) {
|
||||
console.log('WebSocket connected');
|
||||
}
|
||||
};
|
||||
socket.onclose = () => {
|
||||
if (this.webSocket === socket) {
|
||||
this.webSocket = null;
|
||||
}
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {}
|
||||
if ($app.debugWebSocket) {
|
||||
console.log('WebSocket closed');
|
||||
}
|
||||
workerTimers.setTimeout(() => {
|
||||
if (
|
||||
this.isLoggedIn &&
|
||||
$app.friendLogInitStatus &&
|
||||
this.webSocket === null
|
||||
) {
|
||||
this.getAuth();
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
socket.onerror = () => {
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: 'WebSocket Error'
|
||||
}).show();
|
||||
socket.onclose();
|
||||
};
|
||||
socket.onmessage = ({ data }) => {
|
||||
try {
|
||||
if (this.lastWebSocketMessage === data) {
|
||||
// pls no spam
|
||||
return;
|
||||
}
|
||||
this.lastWebSocketMessage = data;
|
||||
var json = JSON.parse(data);
|
||||
try {
|
||||
json.content = JSON.parse(json.content);
|
||||
} catch (err) {}
|
||||
this.$emit('PIPELINE', {
|
||||
json
|
||||
});
|
||||
if ($app.debugWebSocket && json.content) {
|
||||
var displayName = '';
|
||||
var user = this.cachedUsers.get(json.content.userId);
|
||||
if (user) {
|
||||
displayName = user.displayName;
|
||||
}
|
||||
console.log(
|
||||
'WebSocket',
|
||||
json.type,
|
||||
displayName,
|
||||
json.content
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
this.webSocket = socket;
|
||||
};
|
||||
|
||||
API.$on('LOGOUT', function () {
|
||||
this.closeWebSocket();
|
||||
});
|
||||
|
||||
API.closeWebSocket = function () {
|
||||
var socket = this.webSocket;
|
||||
if (socket === null) {
|
||||
return;
|
||||
}
|
||||
this.webSocket = null;
|
||||
try {
|
||||
socket.close();
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
API.reconnectWebSocket = function () {
|
||||
if (!this.isLoggedIn || !$app.friendLogInitStatus) {
|
||||
return;
|
||||
}
|
||||
this.closeWebSocket();
|
||||
this.getAuth();
|
||||
};
|
||||
|
||||
API.$on('PIPELINE', function (args) {
|
||||
var { type, content, err } = args.json;
|
||||
if (typeof err !== 'undefined') {
|
||||
console.error('PIPELINE: error', args);
|
||||
if (this.errorNoty) {
|
||||
this.errorNoty.close();
|
||||
}
|
||||
this.errorNoty = new Noty({
|
||||
type: 'error',
|
||||
text: $app.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':
|
||||
this.$emit('NOTIFICATION', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
this.$emit('PIPELINE:NOTIFICATION', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2':
|
||||
console.log('notification-v2', content);
|
||||
this.$emit('NOTIFICATION:V2', {
|
||||
json: content,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'notification-v2-delete':
|
||||
console.log('notification-v2-delete', content);
|
||||
for (var id of content.ids) {
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: id
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'notification-v2-update':
|
||||
console.log('notification-v2-update', content);
|
||||
this.$emit('NOTIFICATION:V2:UPDATE', {
|
||||
json: content.updates,
|
||||
params: {
|
||||
notificationId: content.id
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'see-notification':
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'hide-notification':
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'response-notification':
|
||||
this.$emit('NOTIFICATION:HIDE', {
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
this.$emit('NOTIFICATION:SEE', {
|
||||
params: {
|
||||
notificationId: content.notificationId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-add':
|
||||
this.$emit('USER', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
this.$emit('FRIEND:ADD', {
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-delete':
|
||||
this.$emit('FRIEND:DELETE', {
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-online':
|
||||
// Where is instanceId, travelingToWorld, travelingToInstance?
|
||||
// More JANK, what a mess
|
||||
var $location = parseLocation(content.location);
|
||||
var $travelingToLocation = parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (content?.user?.id) {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
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
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$emit('FRIEND:STATE', {
|
||||
json: {
|
||||
state: 'online'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-active':
|
||||
if (content?.user?.id) {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'active',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline',
|
||||
|
||||
...content.user
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$emit('FRIEND:STATE', {
|
||||
json: {
|
||||
state: 'active'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'friend-offline':
|
||||
// more JANK, hell yeah
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
id: content.userId,
|
||||
platform: content.platform,
|
||||
state: 'offline',
|
||||
|
||||
location: 'offline',
|
||||
worldId: 'offline',
|
||||
instanceId: 'offline',
|
||||
travelingToLocation: 'offline',
|
||||
travelingToWorld: 'offline',
|
||||
travelingToInstance: 'offline'
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-update':
|
||||
this.$emit('USER', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'friend-location':
|
||||
var $location = parseLocation(content.location);
|
||||
var $travelingToLocation = parseLocation(
|
||||
content.travelingToLocation
|
||||
);
|
||||
if (!content?.user?.id) {
|
||||
var ref = this.cachedUsers.get(content.userId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
...ref,
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation:
|
||||
content.travelingToLocation,
|
||||
travelingToWorld:
|
||||
$travelingToLocation.worldId,
|
||||
travelingToInstance:
|
||||
$travelingToLocation.instanceId
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.$emit('USER', {
|
||||
json: {
|
||||
location: content.location,
|
||||
worldId: content.worldId,
|
||||
instanceId: $location.instanceId,
|
||||
travelingToLocation: content.travelingToLocation,
|
||||
travelingToWorld: $travelingToLocation.worldId,
|
||||
travelingToInstance:
|
||||
$travelingToLocation.instanceId,
|
||||
...content.user,
|
||||
state: 'online' // JANK
|
||||
},
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user-update':
|
||||
this.$emit('USER:CURRENT', {
|
||||
json: content.user,
|
||||
params: {
|
||||
userId: content.userId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user-location':
|
||||
// update current user location
|
||||
if (content.userId !== this.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
|
||||
|
||||
$app.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':
|
||||
var groupId = content.role.groupId;
|
||||
groupRequest.getGroup({ groupId, includeRoles: true });
|
||||
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;
|
||||
}
|
||||
var groupId = member.groupId;
|
||||
if (
|
||||
$app.groupDialog.visible &&
|
||||
$app.groupDialog.id === groupId
|
||||
) {
|
||||
$app.getGroupDialogGroup(groupId);
|
||||
}
|
||||
this.$emit('GROUP:MEMBER', {
|
||||
json: member,
|
||||
params: {
|
||||
groupId
|
||||
}
|
||||
});
|
||||
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;
|
||||
$app.instanceQueueUpdate(instanceId, position, queueSize);
|
||||
break;
|
||||
|
||||
case 'instance-queue-ready':
|
||||
var instanceId = content.instanceLocation;
|
||||
// var expiry = Date.parse(content.expiry);
|
||||
$app.instanceQueueReady(instanceId);
|
||||
break;
|
||||
|
||||
case 'instance-queue-left':
|
||||
var instanceId = content.instanceLocation;
|
||||
$app.removeQueuedInstance(instanceId);
|
||||
// $app.instanceQueueClear();
|
||||
break;
|
||||
|
||||
case 'content-refresh':
|
||||
var contentType = content.contentType;
|
||||
console.log('content-refresh', content);
|
||||
if (contentType === 'icon') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogIconsLoading
|
||||
) {
|
||||
$app.refreshVRCPlusIconsTable();
|
||||
}
|
||||
} else if (contentType === 'gallery') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogGalleryLoading
|
||||
) {
|
||||
$app.refreshGalleryTable();
|
||||
}
|
||||
} else if (contentType === 'emoji') {
|
||||
if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogEmojisLoading
|
||||
) {
|
||||
$app.refreshEmojiTable();
|
||||
}
|
||||
} else if (contentType === 'sticker') {
|
||||
// on sticker upload
|
||||
} else if (contentType === 'print') {
|
||||
if (
|
||||
$app.autoDeleteOldPrints &&
|
||||
content.actionType === 'created'
|
||||
) {
|
||||
$app.tryDeleteOldPrints();
|
||||
} else if (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogPrintsLoading
|
||||
) {
|
||||
$app.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 (
|
||||
$app.galleryDialogVisible &&
|
||||
!$app.galleryDialogInventoryLoading
|
||||
) {
|
||||
$app.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
|
||||
var noty = {
|
||||
type: 'instance.closed',
|
||||
location: content.instanceLocation,
|
||||
message: 'Instance Closed',
|
||||
created_at: new Date().toJSON()
|
||||
};
|
||||
if (
|
||||
$app.notificationTable.filters[0].value.length === 0 ||
|
||||
$app.notificationTable.filters[0].value.includes(
|
||||
noty.type
|
||||
)
|
||||
) {
|
||||
$app.notifyMenu('notification');
|
||||
}
|
||||
$app.queueNotificationNoty(noty);
|
||||
$app.notificationTable.data.push(noty);
|
||||
$app.updateSharedFeed(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown pipeline type', args.json);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_data = {};
|
||||
|
||||
_methods = {};
|
||||
}
|
||||
73
src/components/AvatarInfo.vue
Normal file
73
src/components/AvatarInfo.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div @click="confirm" class="avatar-info">
|
||||
<span style="margin-right: 5px">{{ avatarName }}</span>
|
||||
<span v-if="avatarType" :class="color" style="margin-right: 5px">{{ avatarType }}</span>
|
||||
<span v-if="avatarTags" style="color: #909399; font-family: monospace; font-size: 12px">{{ avatarTags }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { useAvatarStore } from '../stores';
|
||||
|
||||
const avatarStore = useAvatarStore();
|
||||
|
||||
const props = defineProps({
|
||||
imageurl: String,
|
||||
userid: String,
|
||||
hintownerid: String,
|
||||
hintavatarname: String,
|
||||
avatartags: Array
|
||||
});
|
||||
|
||||
const avatarName = ref('');
|
||||
const avatarType = ref('');
|
||||
const avatarTags = ref('');
|
||||
const color = ref('');
|
||||
let ownerId = '';
|
||||
|
||||
const parse = async () => {
|
||||
ownerId = '';
|
||||
avatarName.value = '';
|
||||
avatarType.value = '';
|
||||
color.value = '';
|
||||
avatarTags.value = '';
|
||||
|
||||
if (!props.imageurl) {
|
||||
avatarName.value = '-';
|
||||
} else if (props.hintownerid) {
|
||||
avatarName.value = props.hintavatarname;
|
||||
ownerId = props.hintownerid;
|
||||
} else {
|
||||
try {
|
||||
const info = await avatarStore.getAvatarName(props.imageurl);
|
||||
avatarName.value = info.avatarName;
|
||||
ownerId = info.ownerId;
|
||||
} catch {
|
||||
console.error('Failed to fetch avatar name');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof props.userid === 'undefined' || !ownerId) {
|
||||
color.value = '';
|
||||
avatarType.value = '';
|
||||
} else if (ownerId === props.userid) {
|
||||
color.value = 'avatar-info-own';
|
||||
avatarType.value = '(own)';
|
||||
} else {
|
||||
color.value = 'avatar-info-public';
|
||||
avatarType.value = '(public)';
|
||||
}
|
||||
|
||||
if (Array.isArray(props.avatartags)) {
|
||||
avatarTags.value = props.avatartags.map((tag) => tag.replace('content_', '')).join(', ');
|
||||
}
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
if (!props.imageurl) return;
|
||||
avatarStore.showAvatarAuthorDialog(props.userid, ownerId, props.imageurl);
|
||||
};
|
||||
|
||||
watch([() => props.imageurl, () => props.userid, () => props.avatartags], parse, { immediate: true });
|
||||
</script>
|
||||
33
src/components/CountdownTimer.vue
Normal file
33
src/components/CountdownTimer.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { timeToText } from '../shared/utils';
|
||||
|
||||
const props = defineProps({
|
||||
datetime: { type: String, default: '' },
|
||||
hours: { type: Number, default: 1 }
|
||||
});
|
||||
|
||||
const text = ref('');
|
||||
|
||||
function update() {
|
||||
const epoch = new Date(props.datetime).getTime() + 1000 * 60 * 60 * props.hours - Date.now();
|
||||
text.value = epoch >= 0 ? timeToText(epoch) : '-';
|
||||
}
|
||||
|
||||
watch(() => props.datetime, update);
|
||||
|
||||
onMounted(() => {
|
||||
update();
|
||||
});
|
||||
|
||||
const timer = workerTimers.setInterval(update, 5000);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
workerTimers.clearInterval(timer);
|
||||
});
|
||||
</script>
|
||||
41
src/components/DisplayName.vue
Normal file
41
src/components/DisplayName.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<span @click="showUserDialog" class="x-link">{{ username }}</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { userRequest } from '../api';
|
||||
import { useUserStore } from '../stores';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps({
|
||||
userid: String,
|
||||
location: String,
|
||||
forceUpdateKey: Number,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const username = ref(props.userid);
|
||||
|
||||
async function parse() {
|
||||
username.value = props.userid;
|
||||
if (props.hint) {
|
||||
username.value = props.hint;
|
||||
} else if (props.userid) {
|
||||
const args = await userRequest.getCachedUser({ userId: props.userid });
|
||||
if (args?.json?.displayName) {
|
||||
username.value = args.json.displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showUserDialog() {
|
||||
userStore.showUserDialog(props.userid);
|
||||
}
|
||||
|
||||
watch([() => props.userid, () => props.location, () => props.forceUpdateKey], parse, { immediate: true });
|
||||
</script>
|
||||
@@ -21,14 +21,12 @@
|
||||
</span>
|
||||
<template v-else-if="isGroupByInstance">
|
||||
<i v-if="isFriendTraveling" class="el-icon el-icon-loading"></i>
|
||||
<timer
|
||||
<Timer
|
||||
class="extra"
|
||||
:epoch="epoch"
|
||||
:style="
|
||||
isFriendTraveling ? { display: 'inline-block', overflow: 'unset' } : undefined
|
||||
"></timer>
|
||||
:style="isFriendTraveling ? { display: 'inline-block', overflow: 'unset' } : undefined" />
|
||||
</template>
|
||||
<location
|
||||
<Location
|
||||
v-else
|
||||
class="extra"
|
||||
:location="friend.ref.location"
|
||||
@@ -37,7 +35,7 @@
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="!friend.ref && !API.isRefreshFriendsLoading">
|
||||
<template v-else-if="!friend.ref && !isRefreshFriendsLoading">
|
||||
<span>{{ friend.name || friend.id }}</span>
|
||||
<el-button
|
||||
ttype="text"
|
||||
@@ -62,38 +60,25 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Location from './Location.vue';
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed } from 'vue';
|
||||
import { userImage, userStatusClass } from '../shared/utils';
|
||||
import { useAppearanceSettingsStore, useFriendStore } from '../stores';
|
||||
|
||||
export default {
|
||||
name: 'FriendItem',
|
||||
components: {
|
||||
Location
|
||||
},
|
||||
inject: ['API', 'userImage', 'userStatusClass'],
|
||||
props: {
|
||||
friend: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
hideNicknames: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isGroupByInstance: Boolean
|
||||
},
|
||||
computed: {
|
||||
isFriendTraveling() {
|
||||
return this.friend.ref.location === 'traveling';
|
||||
},
|
||||
isFriendActiveOrOffline() {
|
||||
return this.friend.state === 'active' || this.friend.state === 'offline';
|
||||
},
|
||||
epoch() {
|
||||
return this.isFriendTraveling ? this.friend.ref.$travelingToTime : this.friend.ref.$location_at;
|
||||
}
|
||||
}
|
||||
};
|
||||
const props = defineProps({
|
||||
friend: { type: Object, required: true },
|
||||
isGroupByInstance: Boolean
|
||||
});
|
||||
|
||||
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
||||
|
||||
const isFriendTraveling = computed(() => props.friend.ref.location === 'traveling');
|
||||
const isFriendActiveOrOffline = computed(() => props.friend.state === 'active' || props.friend.state === 'offline');
|
||||
const epoch = computed(() =>
|
||||
isFriendTraveling.value ? props.friend.ref.$travelingToTime : props.friend.ref.$location_at
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
170
src/components/InstanceInfo.vue
Normal file
170
src/components/InstanceInfo.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div style="display: inline-block; margin-left: 5px">
|
||||
<el-tooltip v-if="state.isValidInstance" placement="bottom">
|
||||
<template #content>
|
||||
<div>
|
||||
<span v-if="state.isClosed">Closed At: {{ formatDateFilter(state.closedAt, 'long') }}<br /></span>
|
||||
<template v-if="state.canCloseInstance">
|
||||
<el-button
|
||||
:disabled="state.isClosed"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="closeInstance(props.location)">
|
||||
{{ t('dialog.user.info.close_instance') }} </el-button
|
||||
><br /><br />
|
||||
</template>
|
||||
<span><span style="color: #409eff">PC: </span>{{ state.platforms.standalonewindows }}</span
|
||||
><br />
|
||||
<span><span style="color: #67c23a">Android: </span>{{ state.platforms.android }}</span
|
||||
><br />
|
||||
<span>{{ t('dialog.user.info.instance_game_version') }} {{ state.gameServerVersion }}</span
|
||||
><br />
|
||||
<span v-if="state.queueEnabled">{{ t('dialog.user.info.instance_queuing_enabled') }}<br /></span>
|
||||
<span v-if="state.disabledContentSettings"
|
||||
>{{ t('dialog.user.info.instance_disabled_content') }} {{ state.disabledContentSettings }}<br
|
||||
/></span>
|
||||
<span v-if="state.userList.length">{{ t('dialog.user.info.instance_users') }}<br /></span>
|
||||
<template v-for="user in state.userList">
|
||||
<span
|
||||
style="cursor: pointer; margin-right: 5px"
|
||||
@click="showUserDialog(user.id)"
|
||||
:key="user.id"
|
||||
>{{ user.displayName }}</span
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<i class="el-icon-caret-bottom"></i>
|
||||
</el-tooltip>
|
||||
<span v-if="state.occupants" style="margin-left: 5px">{{ state.occupants }}/{{ state.capacity }}</span>
|
||||
<span v-if="props.friendcount" style="margin-left: 5px">({{ props.friendcount }})</span>
|
||||
<span v-if="state.isFull" style="margin-left: 5px; color: lightcoral">{{
|
||||
t('dialog.user.info.instance_full')
|
||||
}}</span>
|
||||
<span v-if="state.isHardClosed" style="margin-left: 5px; color: lightcoral">{{
|
||||
t('dialog.user.info.instance_hard_closed')
|
||||
}}</span>
|
||||
<span v-else-if="state.isClosed" style="margin-left: 5px; color: lightcoral">{{
|
||||
t('dialog.user.info.instance_closed')
|
||||
}}</span>
|
||||
<span v-if="state.queueSize" style="margin-left: 5px"
|
||||
>{{ t('dialog.user.info.instance_queue') }} {{ state.queueSize }}</span
|
||||
>
|
||||
<span v-if="state.isAgeGated" style="margin-left: 5px; color: lightcoral">{{
|
||||
t('dialog.user.info.instance_age_gated')
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, reactive, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { miscRequest } from '../api';
|
||||
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
|
||||
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const locationStore = useLocationStore();
|
||||
const userStore = useUserStore();
|
||||
const groupStore = useGroupStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
instance: Object,
|
||||
friendcount: Number
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
isValidInstance: false,
|
||||
isFull: false,
|
||||
isClosed: false,
|
||||
isHardClosed: false,
|
||||
closedAt: '',
|
||||
occupants: 0,
|
||||
capacity: 0,
|
||||
queueSize: 0,
|
||||
queueEnabled: false,
|
||||
platforms: [],
|
||||
userList: [],
|
||||
gameServerVersion: '',
|
||||
canCloseInstance: false,
|
||||
isAgeGated: false,
|
||||
disabledContentSettings: ''
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
function parse() {
|
||||
Object.assign(state, {
|
||||
isValidInstance: false,
|
||||
isFull: false,
|
||||
isClosed: false,
|
||||
isHardClosed: false,
|
||||
closedAt: '',
|
||||
occupants: 0,
|
||||
capacity: 0,
|
||||
queueSize: 0,
|
||||
queueEnabled: false,
|
||||
platforms: [],
|
||||
userList: [],
|
||||
gameServerVersion: '',
|
||||
canCloseInstance: false,
|
||||
isAgeGated: false,
|
||||
disabledContentSettings: ''
|
||||
});
|
||||
|
||||
if (!props.location || !props.instance || Object.keys(props.instance).length === 0) return;
|
||||
|
||||
state.isValidInstance = true;
|
||||
state.isFull = props.instance.hasCapacityForYou === false;
|
||||
if (props.instance.closedAt) {
|
||||
state.isClosed = true;
|
||||
state.closedAt = props.instance.closedAt;
|
||||
}
|
||||
state.isHardClosed = props.instance.hardClose === true;
|
||||
state.occupants = props.instance.userCount;
|
||||
if (props.location === locationStore.lastLocation.location) {
|
||||
state.occupants = locationStore.lastLocation.playerList.size;
|
||||
}
|
||||
state.capacity = props.instance.capacity;
|
||||
state.gameServerVersion = props.instance.gameServerVersion;
|
||||
state.queueSize = props.instance.queueSize;
|
||||
if (props.instance.platforms) state.platforms = props.instance.platforms;
|
||||
if (props.instance.users) state.userList = props.instance.users;
|
||||
if (props.instance.ownerId === userStore.currentUser.id) {
|
||||
state.canCloseInstance = true;
|
||||
} else if (props.instance.ownerId?.startsWith('grp_')) {
|
||||
const group = groupStore.cachedGroups.get(props.instance.ownerId);
|
||||
state.canCloseInstance = hasGroupPermission(group, 'group-instance-moderate');
|
||||
}
|
||||
state.isAgeGated = props.instance.ageGate === true;
|
||||
if (props.location?.includes('~ageGate')) state.isAgeGated = true;
|
||||
if (props.instance.$disabledContentSettings?.length) {
|
||||
state.disabledContentSettings = props.instance.$disabledContentSettings.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
watch([() => props.location, () => props.instance, () => props.friendcount], parse, { immediate: true });
|
||||
|
||||
function showUserDialog(userId) {
|
||||
userStore.showUserDialog(userId);
|
||||
}
|
||||
|
||||
function closeInstance(location) {
|
||||
proxy.$confirm('Continue? Close Instance, nobody will be able to join', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
callback: async (action) => {
|
||||
if (action !== 'confirm') return;
|
||||
const args = await miscRequest.closeInstance({ location, hardClose: false });
|
||||
if (args.json) {
|
||||
proxy.$message({ message: t('message.instance.closed'), type: 'success' });
|
||||
instanceStore.applyInstance(args.json);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
34
src/components/InviteYourself.vue
Normal file
34
src/components/InviteYourself.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<el-button v-show="isVisible" @click="confirmInvite" size="mini" icon="el-icon-message" circle />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance } from 'vue';
|
||||
import { instanceRequest } from '../api';
|
||||
import { checkCanInviteSelf, parseLocation } from '../shared/utils';
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
shortname: String
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const isVisible = computed(() => checkCanInviteSelf(props.location));
|
||||
|
||||
function confirmInvite() {
|
||||
const L = parseLocation(props.location);
|
||||
if (!L.isRealInstance) return;
|
||||
|
||||
instanceRequest
|
||||
.selfInvite({
|
||||
instanceId: L.instanceId,
|
||||
worldId: L.worldId,
|
||||
shortName: props.shortname
|
||||
})
|
||||
.then((args) => {
|
||||
proxy.$message({ message: 'Self invite sent', type: 'success' });
|
||||
return args;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
32
src/components/LastJoin.vue
Normal file
32
src/components/LastJoin.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<span v-if="lastJoin">
|
||||
<el-tooltip placement="top" style="margin-left: 5px">
|
||||
<template #content>
|
||||
<span>{{ $t('dialog.user.info.last_join') }} <Timer :epoch="lastJoin" /></span>
|
||||
</template>
|
||||
<i class="el-icon el-icon-location-outline" style="display: inline-block" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useInstanceStore } from '../stores';
|
||||
|
||||
const { instanceJoinHistory } = storeToRefs(useInstanceStore());
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
currentlocation: String
|
||||
});
|
||||
|
||||
const lastJoin = ref(null);
|
||||
|
||||
function parse() {
|
||||
lastJoin.value = instanceJoinHistory.value.get(props.location);
|
||||
}
|
||||
|
||||
watch(() => props.location, parse, { immediate: true });
|
||||
watch(() => props.currentlocation, parse);
|
||||
</script>
|
||||
32
src/components/Launch.vue
Normal file
32
src/components/Launch.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-tooltip
|
||||
v-show="isVisible"
|
||||
placement="top"
|
||||
:content="t('dialog.user.info.launch_invite_tooltip')"
|
||||
:disabled="hideTooltips">
|
||||
<el-button @click="confirm" size="mini" icon="el-icon-switch-button" circle />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { checkCanInviteSelf } from '../shared/utils';
|
||||
import { useLaunchStore } from '../stores';
|
||||
|
||||
const launchStore = useLaunchStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
hideTooltips: Boolean
|
||||
});
|
||||
|
||||
const isVisible = computed(() => {
|
||||
return checkCanInviteSelf(props.location);
|
||||
});
|
||||
|
||||
function confirm() {
|
||||
launchStore.showLaunchDialog(props.location);
|
||||
}
|
||||
</script>
|
||||
@@ -15,147 +15,140 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { parseLocation } from '../composables/instance/utils';
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, watch } from 'vue';
|
||||
import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
|
||||
import { useGroupStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores';
|
||||
|
||||
export default {
|
||||
// eslint-disable-next-line vue/multi-word-component-names
|
||||
name: 'Location',
|
||||
inject: {
|
||||
// prevent randomly error
|
||||
// not good idea, it's temporary
|
||||
API: { default: window.API },
|
||||
getWorldName: { default: window.$app?.getWorldName },
|
||||
getGroupName: { default: window.$app?.getGroupName },
|
||||
showWorldDialog: { default: window.$app?.showWorldDialog },
|
||||
showGroupDialog: { default: window.$app?.showGroupDialog }
|
||||
const { cachedWorlds } = storeToRefs(useWorldStore());
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
const { showGroupDialog } = useGroupStore();
|
||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||
const { verifyShortName } = useSearchStore();
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
traveling: String,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
props: {
|
||||
location: String,
|
||||
traveling: String,
|
||||
hint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isOpenPreviousInstanceInfoDialog: Boolean
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: '',
|
||||
region: this.region,
|
||||
strict: this.strict,
|
||||
isTraveling: this.isTraveling,
|
||||
groupName: this.groupName
|
||||
};
|
||||
link: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
watch: {
|
||||
location() {
|
||||
this.parse();
|
||||
isOpenPreviousInstanceInfoDialog: Boolean
|
||||
});
|
||||
|
||||
const text = ref('');
|
||||
const region = ref('');
|
||||
const strict = ref(false);
|
||||
const isTraveling = ref(false);
|
||||
const groupName = ref('');
|
||||
|
||||
watch(
|
||||
() => props.location,
|
||||
() => {
|
||||
parse();
|
||||
}
|
||||
);
|
||||
|
||||
parse();
|
||||
|
||||
function parse() {
|
||||
isTraveling.value = false;
|
||||
groupName.value = '';
|
||||
let instanceId = props.location;
|
||||
if (typeof props.traveling !== 'undefined' && props.location === 'traveling') {
|
||||
instanceId = props.traveling;
|
||||
isTraveling.value = true;
|
||||
}
|
||||
const L = parseLocation(instanceId);
|
||||
if (L.isOffline) {
|
||||
text.value = 'Offline';
|
||||
} else if (L.isPrivate) {
|
||||
text.value = 'Private';
|
||||
} else if (L.isTraveling) {
|
||||
text.value = 'Traveling';
|
||||
} else if (typeof props.hint === 'string' && props.hint !== '') {
|
||||
if (L.instanceId) {
|
||||
text.value = `${props.hint} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
text.value = props.hint;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parse();
|
||||
},
|
||||
methods: {
|
||||
parse() {
|
||||
if (!this.API) return;
|
||||
this.isTraveling = false;
|
||||
this.groupName = '';
|
||||
let instanceId = this.location;
|
||||
if (typeof this.traveling !== 'undefined' && this.location === 'traveling') {
|
||||
instanceId = this.traveling;
|
||||
this.isTraveling = true;
|
||||
}
|
||||
const L = parseLocation(instanceId);
|
||||
if (L.isOffline) {
|
||||
this.text = 'Offline';
|
||||
} else if (L.isPrivate) {
|
||||
this.text = 'Private';
|
||||
} else if (L.isTraveling) {
|
||||
this.text = 'Traveling';
|
||||
} else if (typeof this.hint === 'string' && this.hint !== '') {
|
||||
if (L.instanceId) {
|
||||
this.text = `${this.hint} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = this.hint;
|
||||
}
|
||||
} else if (L.worldId) {
|
||||
var ref = this.API.cachedWorlds.get(L.worldId);
|
||||
if (typeof ref === 'undefined') {
|
||||
this.getWorldName(L.worldId).then((worldName) => {
|
||||
if (L.tag === instanceId) {
|
||||
if (L.instanceId) {
|
||||
this.text = `${worldName} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = worldName;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (L.instanceId) {
|
||||
this.text = `${ref.name} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
this.text = ref.name;
|
||||
}
|
||||
}
|
||||
if (this.grouphint) {
|
||||
this.groupName = this.grouphint;
|
||||
} else if (L.groupId) {
|
||||
this.groupName = L.groupId;
|
||||
this.getGroupName(instanceId).then((groupName) => {
|
||||
if (L.tag === instanceId) {
|
||||
this.groupName = groupName;
|
||||
} else if (L.worldId) {
|
||||
const ref = cachedWorlds.value.get(L.worldId);
|
||||
if (typeof ref === 'undefined') {
|
||||
getWorldName(L.worldId).then((worldName) => {
|
||||
if (L.tag === instanceId) {
|
||||
if (L.instanceId) {
|
||||
text.value = `${worldName} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
text.value = worldName;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.region = '';
|
||||
if (!L.isOffline && !L.isPrivate && !L.isTraveling) {
|
||||
this.region = L.region;
|
||||
if (!L.region && L.instanceId) {
|
||||
this.region = 'us';
|
||||
}
|
||||
}
|
||||
this.strict = L.strict;
|
||||
},
|
||||
handleShowWorldDialog() {
|
||||
if (this.link) {
|
||||
let instanceId = this.location;
|
||||
if (this.traveling && this.location === 'traveling') {
|
||||
instanceId = this.traveling;
|
||||
}
|
||||
if (!instanceId && this.hint.length === 8) {
|
||||
// shortName
|
||||
this.API.$emit('SHOW_WORLD_DIALOG_SHORTNAME', this.hint);
|
||||
return;
|
||||
}
|
||||
if (this.isOpenPreviousInstanceInfoDialog) {
|
||||
this.$emit('open-previous-instance-info-dialog', instanceId);
|
||||
} else {
|
||||
this.showWorldDialog(instanceId);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleShowGroupDialog() {
|
||||
let location = this.location;
|
||||
if (this.isTraveling) {
|
||||
location = this.traveling;
|
||||
}
|
||||
if (!location || !this.link) {
|
||||
return;
|
||||
}
|
||||
const L = parseLocation(location);
|
||||
if (!L.groupId) {
|
||||
return;
|
||||
}
|
||||
this.showGroupDialog(L.groupId);
|
||||
});
|
||||
} else if (L.instanceId) {
|
||||
text.value = `${ref.name} #${L.instanceName} ${L.accessTypeName}`;
|
||||
} else {
|
||||
text.value = ref.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (props.grouphint) {
|
||||
groupName.value = props.grouphint;
|
||||
} else if (L.groupId) {
|
||||
groupName.value = L.groupId;
|
||||
getGroupName(instanceId).then((name) => {
|
||||
if (L.tag === instanceId) {
|
||||
groupName.value = name;
|
||||
}
|
||||
});
|
||||
}
|
||||
region.value = '';
|
||||
if (!L.isOffline && !L.isPrivate && !L.isTraveling) {
|
||||
region.value = L.region;
|
||||
if (!L.region && L.instanceId) {
|
||||
region.value = 'us';
|
||||
}
|
||||
}
|
||||
strict.value = L.strict;
|
||||
}
|
||||
|
||||
function handleShowWorldDialog() {
|
||||
if (props.link) {
|
||||
let instanceId = props.location;
|
||||
if (props.traveling && props.location === 'traveling') {
|
||||
instanceId = props.traveling;
|
||||
}
|
||||
if (!instanceId && props.hint.length === 8) {
|
||||
verifyShortName('', props.hint);
|
||||
return;
|
||||
}
|
||||
if (props.isOpenPreviousInstanceInfoDialog) {
|
||||
showPreviousInstancesInfoDialog(instanceId);
|
||||
} else {
|
||||
showWorldDialog(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleShowGroupDialog() {
|
||||
let location = props.location;
|
||||
if (isTraveling.value) {
|
||||
location = props.traveling;
|
||||
}
|
||||
if (!location || !props.link) {
|
||||
return;
|
||||
}
|
||||
const L = parseLocation(location);
|
||||
if (!L.groupId) {
|
||||
return;
|
||||
}
|
||||
showGroupDialog(L.groupId);
|
||||
}
|
||||
</script>
|
||||
|
||||
78
src/components/LocationWorld.vue
Normal file
78
src/components/LocationWorld.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<span>
|
||||
<span @click="showLaunchDialog" class="x-link">
|
||||
<i v-if="isUnlocked" class="el-icon el-icon-unlock" style="display: inline-block; margin-right: 5px"></i>
|
||||
<span>#{{ instanceName }} {{ accessTypeName }}</span>
|
||||
</span>
|
||||
<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span>
|
||||
<span class="flags" :class="region" style="display: inline-block; margin-left: 5px"></span>
|
||||
<i v-if="strict" class="el-icon el-icon-lock" style="display: inline-block; margin-left: 5px"></i>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { getGroupName, parseLocation } from '../shared/utils';
|
||||
import { useGroupStore, useLaunchStore } from '../stores';
|
||||
|
||||
const launchStore = useLaunchStore();
|
||||
const groupStore = useGroupStore();
|
||||
|
||||
const props = defineProps({
|
||||
locationobject: Object,
|
||||
currentuserid: String,
|
||||
worlddialogshortname: String,
|
||||
grouphint: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const location = ref('');
|
||||
const instanceName = ref('');
|
||||
const accessTypeName = ref('');
|
||||
const region = ref('us');
|
||||
const shortName = ref('');
|
||||
const isUnlocked = ref(false);
|
||||
const strict = ref(false);
|
||||
const groupName = ref('');
|
||||
|
||||
function parse() {
|
||||
const locObj = props.locationobject;
|
||||
location.value = locObj.tag;
|
||||
instanceName.value = locObj.instanceName;
|
||||
accessTypeName.value = locObj.accessTypeName;
|
||||
strict.value = locObj.strict;
|
||||
shortName.value = locObj.shortName;
|
||||
|
||||
isUnlocked.value =
|
||||
(props.worlddialogshortname && locObj.shortName && props.worlddialogshortname === locObj.shortName) ||
|
||||
props.currentuserid === locObj.userId;
|
||||
|
||||
region.value = locObj.region || 'us';
|
||||
|
||||
if (props.grouphint) {
|
||||
groupName.value = props.grouphint;
|
||||
} else if (locObj.groupId) {
|
||||
groupName.value = locObj.groupId;
|
||||
getGroupName(locObj.groupId).then((name) => {
|
||||
groupName.value = name;
|
||||
});
|
||||
} else {
|
||||
groupName.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.locationobject, parse, { immediate: true });
|
||||
|
||||
function showLaunchDialog() {
|
||||
launchStore.showLaunchDialog(location.value, shortName.value);
|
||||
}
|
||||
|
||||
function showGroupDialog() {
|
||||
if (!location.value) return;
|
||||
const L = parseLocation(location.value);
|
||||
if (!L.groupId) return;
|
||||
groupStore.showGroupDialog(L.groupId);
|
||||
}
|
||||
</script>
|
||||
@@ -1,104 +1,65 @@
|
||||
<template>
|
||||
<el-menu
|
||||
ref="navRef"
|
||||
collapse
|
||||
@select="$emit('select', $event)"
|
||||
:default-active="menuActiveIndex"
|
||||
:collapse-transition="false">
|
||||
<el-menu-item index="feed">
|
||||
<i class="el-icon-news"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.feed') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="gameLog">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.game_log') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="playerList">
|
||||
<i class="el-icon-tickets"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.player_list') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="search">
|
||||
<i class="el-icon-search"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.search') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="favorite">
|
||||
<i class="el-icon-star-off"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.favorites') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="friendLog">
|
||||
<i class="el-icon-notebook-2"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.friend_log') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="moderation">
|
||||
<i class="el-icon-finished"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.moderation') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="notification">
|
||||
<i class="el-icon-bell"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.notification') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="friendList">
|
||||
<i class="el-icon-s-management"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.friend_list') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="charts">
|
||||
<i class="el-icon-data-analysis"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.charts') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="profile">
|
||||
<i class="el-icon-user"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.profile') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="settings">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<template slot="title">
|
||||
<span>{{ $t('nav_tooltip.settings') }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<div class="x-menu-container">
|
||||
<div v-if="updateInProgress" class="pending-update" @click="showVRCXUpdateDialog">
|
||||
<el-progress
|
||||
type="circle"
|
||||
width="50"
|
||||
stroke-width="3"
|
||||
:percentage="updateProgress"
|
||||
:format="updateProgressText"></el-progress>
|
||||
</div>
|
||||
<div v-else-if="pendingVRCXUpdate || pendingVRCXInstall" class="pending-update">
|
||||
<el-button
|
||||
type="default"
|
||||
size="mini"
|
||||
icon="el-icon-download"
|
||||
circle
|
||||
style="font-size: 14px; height: 50px; width: 50px"
|
||||
@click="showVRCXUpdateDialog" />
|
||||
</div>
|
||||
<el-menu
|
||||
ref="navRef"
|
||||
collapse
|
||||
:default-active="menuActiveIndex"
|
||||
:collapse-transition="false"
|
||||
@select="selectMenu">
|
||||
<el-menu-item
|
||||
v-for="item in navItems"
|
||||
:key="item.index"
|
||||
:index="item.index"
|
||||
:class="{ notify: notifiedMenus.includes(item.index) }">
|
||||
<i :class="item.icon" />
|
||||
<template #title>
|
||||
<span>{{ $t(item.tooltip) }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NavMenu',
|
||||
props: {
|
||||
menuActiveIndex: {
|
||||
type: String,
|
||||
default: 'feed'
|
||||
}
|
||||
}
|
||||
};
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useUiStore, useVRCXUpdaterStore } from '../stores';
|
||||
|
||||
const navItems = [
|
||||
{ index: 'feed', icon: 'el-icon-news', tooltip: 'nav_tooltip.feed' },
|
||||
{ index: 'gameLog', icon: 'el-icon-s-data', tooltip: 'nav_tooltip.game_log' },
|
||||
{ index: 'playerList', icon: 'el-icon-tickets', tooltip: 'nav_tooltip.player_list' },
|
||||
{ index: 'search', icon: 'el-icon-search', tooltip: 'nav_tooltip.search' },
|
||||
{ index: 'favorite', icon: 'el-icon-star-off', tooltip: 'nav_tooltip.favorites' },
|
||||
{ index: 'friendLog', icon: 'el-icon-notebook-2', tooltip: 'nav_tooltip.friend_log' },
|
||||
{ index: 'moderation', icon: 'el-icon-finished', tooltip: 'nav_tooltip.moderation' },
|
||||
{ index: 'notification', icon: 'el-icon-bell', tooltip: 'nav_tooltip.notification' },
|
||||
{ index: 'friendList', icon: 'el-icon-s-management', tooltip: 'nav_tooltip.friend_list' },
|
||||
{ index: 'charts', icon: 'el-icon-data-analysis', tooltip: 'nav_tooltip.charts' },
|
||||
{ index: 'profile', icon: 'el-icon-user', tooltip: 'nav_tooltip.profile' },
|
||||
{ index: 'settings', icon: 'el-icon-s-tools', tooltip: 'nav_tooltip.settings' }
|
||||
];
|
||||
|
||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||
const { pendingVRCXUpdate, pendingVRCXInstall, updateInProgress, updateProgress } = storeToRefs(VRCXUpdaterStore);
|
||||
const { showVRCXUpdateDialog, updateProgressText } = VRCXUpdaterStore;
|
||||
const uiStore = useUiStore();
|
||||
const { menuActiveIndex, notifiedMenus } = storeToRefs(uiStore);
|
||||
const { selectMenu } = uiStore;
|
||||
</script>
|
||||
|
||||
@@ -11,35 +11,23 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SimpleSwitch',
|
||||
props: {
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
value: {
|
||||
type: Boolean
|
||||
},
|
||||
tooltip: {
|
||||
type: String
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean
|
||||
},
|
||||
longLabel: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change(event) {
|
||||
this.$emit('change', event);
|
||||
}
|
||||
}
|
||||
};
|
||||
<script setup>
|
||||
defineProps({
|
||||
label: String,
|
||||
value: Boolean,
|
||||
tooltip: String,
|
||||
disabled: Boolean,
|
||||
longLabel: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
function change(event) {
|
||||
emit('change', event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style scoped>
|
||||
.simple-switch {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
|
||||
29
src/components/Timer.vue
Normal file
29
src/components/Timer.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { timeToText } from '../shared/utils';
|
||||
|
||||
const props = defineProps({
|
||||
epoch: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const now = ref(Date.now());
|
||||
const text = computed(() => {
|
||||
return props.epoch ? timeToText(now.value - props.epoch) : '-';
|
||||
});
|
||||
|
||||
let timerId = null;
|
||||
onMounted(() => {
|
||||
timerId = setInterval(() => {
|
||||
now.value = Date.now();
|
||||
}, 5000);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timerId);
|
||||
});
|
||||
</script>
|
||||
@@ -261,7 +261,7 @@
|
||||
type="default"
|
||||
icon="el-icon-check"
|
||||
circle
|
||||
:disabled="API.currentUser.currentAvatar === avatarDialog.id"
|
||||
:disabled="currentUser.currentAvatar === avatarDialog.id"
|
||||
style="margin-left: 5px"
|
||||
@click="selectAvatar(avatarDialog.id)"></el-button>
|
||||
</el-tooltip>
|
||||
@@ -299,12 +299,12 @@
|
||||
>{{ t('dialog.avatar.actions.select_fallback') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="avatarDialog.ref.authorId !== API.currentUser.id"
|
||||
v-if="avatarDialog.ref.authorId !== currentUser.id"
|
||||
icon="el-icon-picture-outline"
|
||||
command="Previous Images"
|
||||
>{{ t('dialog.avatar.actions.show_previous_images') }}</el-dropdown-item
|
||||
>
|
||||
<template v-if="avatarDialog.ref.authorId === API.currentUser.id">
|
||||
<template v-if="avatarDialog.ref.authorId === currentUser.id">
|
||||
<el-dropdown-item
|
||||
v-if="avatarDialog.ref.releaseStatus === 'public'"
|
||||
icon="el-icon-user-solid"
|
||||
@@ -367,7 +367,7 @@
|
||||
<el-tab-pane :label="t('dialog.avatar.info.header')">
|
||||
<div class="x-friend-list" style="max-height: unset">
|
||||
<div
|
||||
v-if="avatarDialog.galleryImages.length || avatarDialog.ref.authorId === API.currentUser.id"
|
||||
v-if="avatarDialog.galleryImages.length || avatarDialog.ref.authorId === currentUser.id"
|
||||
style="width: 100%">
|
||||
<span class="name">{{ t('dialog.avatar.info.gallery') }}</span>
|
||||
<input
|
||||
@@ -377,8 +377,8 @@
|
||||
style="display: none"
|
||||
@change="onFileChangeAvatarGallery" />
|
||||
<el-button
|
||||
v-if="avatarDialog.ref.authorId === API.currentUser.id"
|
||||
v-loading="avatarDialog.galleryLoading"
|
||||
v-if="avatarDialog.ref.authorId === currentUser.id"
|
||||
:disabled="!!avatarDialog.galleryLoading"
|
||||
size="small"
|
||||
icon="el-icon-upload2"
|
||||
style="margin-left: 5px"
|
||||
@@ -396,7 +396,7 @@
|
||||
style="width: 100%; height: 100%; object-fit: contain"
|
||||
@click="showFullscreenImageDialog(imageUrl)" />
|
||||
<div
|
||||
v-if="avatarDialog.ref.authorId === API.currentUser.id"
|
||||
v-if="avatarDialog.ref.authorId === currentUser.id"
|
||||
style="position: absolute; bottom: 5px; left: 33.3%">
|
||||
<el-button
|
||||
size="mini"
|
||||
@@ -497,16 +497,18 @@
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('dialog.avatar.info.created_at') }}</span>
|
||||
<span class="extra">{{ avatarDialog.ref.created_at | formatDate('long') }}</span>
|
||||
<span class="extra">{{ formatDateFilter(avatarDialog.ref.created_at, 'long') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('dialog.avatar.info.last_updated') }}</span>
|
||||
<span v-if="avatarDialog.lastUpdated" class="extra">{{
|
||||
avatarDialog.lastUpdated | formatDate('long')
|
||||
formatDateFilter(avatarDialog.lastUpdated, 'long')
|
||||
}}</span>
|
||||
<span v-else class="extra">{{
|
||||
formatDateFilter(avatarDialog.ref.updated_at, 'long')
|
||||
}}</span>
|
||||
<span v-else class="extra">{{ avatarDialog.ref.updated_at | formatDate('long') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
@@ -596,71 +598,68 @@
|
||||
<SetAvatarStylesDialog :set-avatar-styles-dialog="setAvatarStylesDialog" />
|
||||
<ChangeAvatarImageDialog
|
||||
:change-avatar-image-dialog-visible.sync="changeAvatarImageDialogVisible"
|
||||
:previous-images-table="previousImagesTable"
|
||||
:avatar-dialog="avatarDialog"
|
||||
:previous-images-file-id="previousImagesFileId"
|
||||
@refresh="displayPreviousImages" />
|
||||
<PreviousImagesDialog
|
||||
:previous-images-dialog-visible.sync="previousImagesDialogVisible"
|
||||
:previous-images-table="previousImagesTable" />
|
||||
<PreviousImagesDialog />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, inject, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { avatarModerationRequest, avatarRequest, favoriteRequest, imageRequest, miscRequest } from '../../../api';
|
||||
import utils from '../../../classes/utils';
|
||||
import { compareUnityVersion, storeAvatarImage } from '../../../composables/avatar/utils';
|
||||
import { database } from '../../../service/database';
|
||||
import {
|
||||
adjustDialogZ,
|
||||
buildTreeData,
|
||||
commaNumber,
|
||||
compareUnityVersion,
|
||||
copyToClipboard,
|
||||
downloadAndSaveJson,
|
||||
extractFileId,
|
||||
extractFileVersion,
|
||||
replaceVrcPackageUrl
|
||||
} from '../../../composables/shared/utils';
|
||||
import database from '../../../service/database';
|
||||
openExternalLink,
|
||||
openFolderGeneric,
|
||||
replaceVrcPackageUrl,
|
||||
storeAvatarImage,
|
||||
timeToText,
|
||||
moveArrayItem,
|
||||
formatDateFilter
|
||||
} from '../../../shared/utils';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useAvatarStore,
|
||||
useFavoriteStore,
|
||||
useGalleryStore,
|
||||
useGameStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import PreviousImagesDialog from '../PreviousImagesDialog.vue';
|
||||
import ChangeAvatarImageDialog from './ChangeAvatarImageDialog.vue';
|
||||
import SetAvatarStylesDialog from './SetAvatarStylesDialog.vue';
|
||||
import SetAvatarTagsDialog from './SetAvatarTagsDialog.vue';
|
||||
|
||||
const API = inject('API');
|
||||
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
|
||||
const showUserDialog = inject('showUserDialog');
|
||||
const showAvatarDialog = inject('showAvatarDialog');
|
||||
const showFavoriteDialog = inject('showFavoriteDialog');
|
||||
const openExternalLink = inject('openExternalLink');
|
||||
const adjustDialogZ = inject('adjustDialogZ');
|
||||
const getImageUrlFromImageId = inject('getImageUrlFromImageId');
|
||||
const getAvatarGallery = inject('getAvatarGallery');
|
||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { showUserDialog, sortUserDialogAvatars } = useUserStore();
|
||||
const { userDialog, currentUser } = storeToRefs(useUserStore());
|
||||
const { avatarDialog, cachedAvatarModerations, cachedAvatars, cachedAvatarNames } = storeToRefs(useAvatarStore());
|
||||
const { showAvatarDialog, getAvatarGallery, applyAvatarModeration, applyAvatar } = useAvatarStore();
|
||||
const { showFavoriteDialog } = useFavoriteStore();
|
||||
const { isGameRunning } = storeToRefs(useGameStore());
|
||||
const { deleteVRChatCache } = useGameStore();
|
||||
const { previousImagesDialogVisible, previousImagesTable } = storeToRefs(useGalleryStore());
|
||||
const { showFullscreenImageDialog, checkPreviousImageAvailable } = useGalleryStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const { $message, $confirm, $prompt } = instance.proxy;
|
||||
|
||||
const emit = defineEmits(['openFolderGeneric', 'deleteVRChatCache', 'openPreviousImagesDialog']);
|
||||
|
||||
const props = defineProps({
|
||||
avatarDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
hideTooltips: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isGameRunning: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
defineEmits(['openPreviousImagesDialog']);
|
||||
|
||||
const avatarDialogRef = ref(null);
|
||||
const changeAvatarImageDialogVisible = ref(false);
|
||||
const previousImagesFileId = ref('');
|
||||
const previousImagesDialogVisible = ref(false);
|
||||
const previousImagesTable = ref([]);
|
||||
|
||||
const treeData = ref([]);
|
||||
const timeSpent = ref(0);
|
||||
@@ -695,7 +694,7 @@
|
||||
});
|
||||
|
||||
const avatarDialogPlatform = computed(() => {
|
||||
const { ref } = props.avatarDialog;
|
||||
const { ref } = avatarDialog.value;
|
||||
const platforms = [];
|
||||
if (ref.unityPackages) {
|
||||
for (const unityPackage of ref.unityPackages) {
|
||||
@@ -721,11 +720,11 @@
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.avatarDialog.loading,
|
||||
() => avatarDialog.value.loading,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
nextTick(() => {
|
||||
const D = props.avatarDialog;
|
||||
const D = avatarDialog.value;
|
||||
if (D.visible) {
|
||||
adjustDialogZ(avatarDialogRef.value.$el);
|
||||
}
|
||||
@@ -735,6 +734,10 @@
|
||||
}
|
||||
);
|
||||
|
||||
function getImageUrlFromImageId(imageId) {
|
||||
return `https://api.vrchat.cloud/api/1/file/${imageId}/1/`;
|
||||
}
|
||||
|
||||
function handleDialogOpen() {
|
||||
fileAnalysis.value = {};
|
||||
memo.value = '';
|
||||
@@ -744,19 +747,19 @@
|
||||
}
|
||||
|
||||
function getAvatarTimeSpent() {
|
||||
const D = props.avatarDialog;
|
||||
const D = avatarDialog.value;
|
||||
database.getAvatarTimeSpent(D.id).then((aviTime) => {
|
||||
if (D.id === aviTime.avatarId) {
|
||||
timeSpent.value = aviTime.timeSpent;
|
||||
if (D.id === API.currentUser.currentAvatar && API.currentUser.$previousAvatarSwapTime) {
|
||||
timeSpent.value += Date.now() - API.currentUser.$previousAvatarSwapTime;
|
||||
if (D.id === currentUser.value.currentAvatar && currentUser.value.$previousAvatarSwapTime) {
|
||||
timeSpent.value += Date.now() - currentUser.value.$previousAvatarSwapTime;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getAvatarMemo() {
|
||||
const D = props.avatarDialog;
|
||||
const D = avatarDialog.value;
|
||||
database.getAvatarMemoDB(D.id).then((res) => {
|
||||
if (D.id === res.avatarId) {
|
||||
memo.value = res.memo;
|
||||
@@ -764,20 +767,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
function openFolderGeneric(path) {
|
||||
emit('openFolderGeneric', path);
|
||||
}
|
||||
|
||||
function deleteVRChatCache(ref) {
|
||||
emit('deleteVRChatCache', ref);
|
||||
}
|
||||
|
||||
function commaNumber(num) {
|
||||
return utils.commaNumber(num);
|
||||
}
|
||||
|
||||
function avatarDialogCommand(command) {
|
||||
const D = props.avatarDialog;
|
||||
const D = avatarDialog.value;
|
||||
switch (command) {
|
||||
case 'Refresh':
|
||||
showAvatarDialog(D.id);
|
||||
@@ -804,7 +795,7 @@
|
||||
showSetAvatarStylesDialog(D.id);
|
||||
break;
|
||||
case 'Download Unity Package':
|
||||
openExternalLink(replaceVrcPackageUrl(props.avatarDialog.ref.unityPackageUrl));
|
||||
openExternalLink(replaceVrcPackageUrl(avatarDialog.value.ref.unityPackageUrl));
|
||||
break;
|
||||
case 'Add Favorite':
|
||||
showFavoriteDialog('avatar', D.id);
|
||||
@@ -845,7 +836,7 @@
|
||||
})
|
||||
.then((args) => {
|
||||
// 'AVATAR-MODERATION';
|
||||
args.ref = API.applyAvatarModeration(args.json);
|
||||
applyAvatarModeration(args.json);
|
||||
$message({
|
||||
message: 'Avatar blocked',
|
||||
type: 'success'
|
||||
@@ -860,9 +851,8 @@
|
||||
targetAvatarId: D.id
|
||||
})
|
||||
.then((args) => {
|
||||
// 'AVATAR-MODERATION:DELETE';
|
||||
API.cachedAvatarModerations.delete(args.params.targetAvatarId);
|
||||
const D = props.avatarDialog;
|
||||
cachedAvatarModerations.value.delete(args.params.targetAvatarId);
|
||||
const D = avatarDialog.value;
|
||||
if (
|
||||
args.params.avatarModerationType === 'block' &&
|
||||
D.id === args.params.targetAvatarId
|
||||
@@ -878,6 +868,7 @@
|
||||
releaseStatus: 'public'
|
||||
})
|
||||
.then((args) => {
|
||||
applyAvatar(args.json);
|
||||
$message({
|
||||
message: 'Avatar updated to public',
|
||||
type: 'success'
|
||||
@@ -892,6 +883,7 @@
|
||||
releaseStatus: 'private'
|
||||
})
|
||||
.then((args) => {
|
||||
applyAvatar(args.json);
|
||||
$message({
|
||||
message: 'Avatar updated to private',
|
||||
type: 'success'
|
||||
@@ -905,6 +897,19 @@
|
||||
avatarId: D.id
|
||||
})
|
||||
.then((args) => {
|
||||
const { json } = args;
|
||||
cachedAvatars.value.delete(json._id);
|
||||
if (userDialog.value.id === json.authorId) {
|
||||
const map = new Map();
|
||||
for (const ref of cachedAvatars.value.values()) {
|
||||
if (ref.authorId === json.authorId) {
|
||||
map.set(ref.id, ref);
|
||||
}
|
||||
}
|
||||
const array = Array.from(map.values());
|
||||
sortUserDialogAvatars(array);
|
||||
}
|
||||
|
||||
$message({
|
||||
message: 'Avatar deleted',
|
||||
type: 'success'
|
||||
@@ -973,7 +978,7 @@
|
||||
function displayPreviousImages(command) {
|
||||
previousImagesTable.value = [];
|
||||
previousImagesFileId.value = '';
|
||||
const { imageUrl } = props.avatarDialog.ref;
|
||||
const { imageUrl } = avatarDialog.value.ref;
|
||||
const fileId = extractFileId(imageUrl);
|
||||
if (!fileId) {
|
||||
return;
|
||||
@@ -988,7 +993,7 @@
|
||||
changeAvatarImageDialogVisible.value = true;
|
||||
}
|
||||
imageRequest.getAvatarImages(params).then((args) => {
|
||||
storeAvatarImage(args);
|
||||
storeAvatarImage(args, cachedAvatarNames.value);
|
||||
previousImagesFileId.value = args.json.id;
|
||||
|
||||
const images = [];
|
||||
@@ -1001,23 +1006,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function checkPreviousImageAvailable(images) {
|
||||
previousImagesTable.value = [];
|
||||
for (const image of images) {
|
||||
if (image.file && image.file.url) {
|
||||
const response = await fetch(image.file.url, {
|
||||
method: 'HEAD',
|
||||
redirect: 'follow'
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
if (response.status === 200) {
|
||||
previousImagesTable.value.push(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectAvatar(id) {
|
||||
avatarRequest
|
||||
.selectAvatar({
|
||||
@@ -1047,6 +1035,7 @@
|
||||
description: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
applyAvatar(args.json);
|
||||
$message({
|
||||
message: t('prompt.change_avatar_description.message.success'),
|
||||
type: 'success'
|
||||
@@ -1073,6 +1062,7 @@
|
||||
name: instance.inputValue
|
||||
})
|
||||
.then((args) => {
|
||||
applyAvatar(args.json);
|
||||
$message({
|
||||
message: t('prompt.rename_avatar.message.success'),
|
||||
type: 'success'
|
||||
@@ -1087,12 +1077,12 @@
|
||||
function onAvatarMemoChange() {
|
||||
if (memo.value) {
|
||||
database.setAvatarMemo({
|
||||
avatarId: props.avatarDialog.id,
|
||||
avatarId: avatarDialog.value.id,
|
||||
editedAt: new Date().toJSON(),
|
||||
memo: memo.value
|
||||
});
|
||||
} else {
|
||||
database.deleteAvatarMemo(props.avatarDialog.id);
|
||||
database.deleteAvatarMemo(avatarDialog.value.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,17 +1094,13 @@
|
||||
copyToClipboard(`https://vrchat.com/home/avatar/${id}`);
|
||||
}
|
||||
|
||||
function timeToText(time) {
|
||||
return utils.timeToText(time);
|
||||
}
|
||||
|
||||
function refreshAvatarDialogTreeData() {
|
||||
treeData.value = utils.buildTreeData(props.avatarDialog.ref);
|
||||
treeData.value = buildTreeData(avatarDialog.value.ref);
|
||||
}
|
||||
|
||||
function getAvatarFileAnalysis() {
|
||||
let unityPackage;
|
||||
const D = props.avatarDialog;
|
||||
const D = avatarDialog.value;
|
||||
const avatarId = D.ref.id;
|
||||
let assetUrl = '';
|
||||
let variant = 'security';
|
||||
@@ -1154,8 +1140,7 @@
|
||||
return;
|
||||
}
|
||||
miscRequest.getFileAnalysis({ fileId, version, variant, avatarId }).then((args) => {
|
||||
// API.$on('FILE:ANALYSIS', function (args) {
|
||||
if (!props.avatarDialog.visible || props.avatarDialog.id !== args.params.avatarId) {
|
||||
if (!avatarDialog.value.visible || avatarDialog.value.id !== args.params.avatarId) {
|
||||
return;
|
||||
}
|
||||
const ref = args.json;
|
||||
@@ -1169,9 +1154,8 @@
|
||||
ref._totalTextureUsage = `${(ref.avatarStats.totalTextureUsage / 1048576).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
fileAnalysis.value = utils.buildTreeData(args.json);
|
||||
fileAnalysis.value = buildTreeData(args.json);
|
||||
});
|
||||
// });
|
||||
}
|
||||
|
||||
function showSetAvatarTagsDialog(avatarId) {
|
||||
@@ -1187,7 +1171,7 @@
|
||||
D.contentViolence = false;
|
||||
D.contentAdult = false;
|
||||
D.contentSex = false;
|
||||
const oldTags = props.avatarDialog.ref.tags;
|
||||
const oldTags = avatarDialog.value.ref.tags;
|
||||
oldTags.forEach((tag) => {
|
||||
switch (tag) {
|
||||
case 'content_horror':
|
||||
@@ -1212,8 +1196,8 @@
|
||||
break;
|
||||
}
|
||||
});
|
||||
for (const ref of API.cachedAvatars.values()) {
|
||||
if (ref.authorId === API.currentUser.id) {
|
||||
for (const ref of cachedAvatars.value.values()) {
|
||||
if (ref.authorId === currentUser.value.id) {
|
||||
ref.$selected = false;
|
||||
ref.$tagString = '';
|
||||
if (avatarId === ref.id) {
|
||||
@@ -1245,12 +1229,12 @@
|
||||
const D = setAvatarStylesDialog;
|
||||
D.visible = true;
|
||||
D.loading = true;
|
||||
D.avatarId = props.avatarDialog.id;
|
||||
D.primaryStyle = props.avatarDialog.ref.styles?.primary || '';
|
||||
D.secondaryStyle = props.avatarDialog.ref.styles?.secondary || '';
|
||||
D.avatarId = avatarDialog.value.id;
|
||||
D.primaryStyle = avatarDialog.value.ref.styles?.primary || '';
|
||||
D.secondaryStyle = avatarDialog.value.ref.styles?.secondary || '';
|
||||
D.initialPrimaryStyle = D.primaryStyle;
|
||||
D.initialSecondaryStyle = D.secondaryStyle;
|
||||
D.initialTags = props.avatarDialog.ref.tags;
|
||||
D.initialTags = avatarDialog.value.ref.tags;
|
||||
D.authorTags = '';
|
||||
for (const tag of D.initialTags) {
|
||||
if (tag.startsWith('author_tag_')) {
|
||||
@@ -1298,43 +1282,31 @@
|
||||
}
|
||||
const r = new FileReader();
|
||||
r.onload = function () {
|
||||
props.avatarDialog.galleryLoading = true;
|
||||
avatarDialog.value.galleryLoading = true;
|
||||
const base64Body = btoa(r.result);
|
||||
avatarRequest
|
||||
.uploadAvatarGalleryImage(base64Body, props.avatarDialog.id)
|
||||
.uploadAvatarGalleryImage(base64Body, avatarDialog.value.id)
|
||||
.then((args) => {
|
||||
$message({
|
||||
message: t('message.avatar_gallery.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
console.log(args);
|
||||
props.avatarDialog.galleryImages = getAvatarGallery(props.avatarDialog.id);
|
||||
avatarDialog.value.galleryImages = getAvatarGallery(avatarDialog.value.id);
|
||||
return args;
|
||||
})
|
||||
.finally(() => {
|
||||
props.avatarDialog.galleryLoading = false;
|
||||
avatarDialog.value.galleryLoading = false;
|
||||
});
|
||||
};
|
||||
r.readAsBinaryString(files[0]);
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function deleteAvatarGalleryImage(imageUrl) {
|
||||
const fileId = extractFileId(imageUrl);
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
$message({
|
||||
message: t('message.avatar_gallery.deleted'),
|
||||
type: 'success'
|
||||
});
|
||||
props.avatarDialog.galleryImages = getAvatarGallery(props.avatarDialog.id);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
function reorderAvatarGalleryImage(imageUrl, direction) {
|
||||
const fileId = extractFileId(imageUrl);
|
||||
let fileIds = [];
|
||||
props.avatarDialog.ref.gallery.forEach((item) => {
|
||||
avatarDialog.value.ref.gallery.forEach((item) => {
|
||||
fileIds.push(extractFileId(item.id));
|
||||
});
|
||||
const index = fileIds.indexOf(fileId);
|
||||
@@ -1360,16 +1332,28 @@
|
||||
return;
|
||||
}
|
||||
if (direction === -1) {
|
||||
utils.moveArrayItem(fileIds, index, index - 1);
|
||||
moveArrayItem(fileIds, index, index - 1);
|
||||
} else {
|
||||
utils.moveArrayItem(fileIds, index, index + 1);
|
||||
moveArrayItem(fileIds, index, index + 1);
|
||||
}
|
||||
avatarRequest.setAvatarGalleryOrder(fileIds).then((args) => {
|
||||
$message({
|
||||
message: t('message.avatar_gallery.reordered'),
|
||||
type: 'success'
|
||||
});
|
||||
props.avatarDialog.galleryImages = getAvatarGallery(props.avatarDialog.id);
|
||||
avatarDialog.value.galleryImages = getAvatarGallery(avatarDialog.value.id);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAvatarGalleryImage(imageUrl) {
|
||||
const fileId = extractFileId(imageUrl);
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
$message({
|
||||
message: t('message.avatar_gallery.deleted'),
|
||||
type: 'success'
|
||||
});
|
||||
getAvatarGallery(avatarDialog.value.id);
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,32 +42,29 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { imageRequest } from '../../../api';
|
||||
import { extractFileId } from '../../../composables/shared/utils';
|
||||
import webApiService from '../../../service/webapi';
|
||||
import { AppGlobal } from '../../../service/appConfig';
|
||||
import { $throw } from '../../../service/request';
|
||||
import { extractFileId } from '../../../shared/utils';
|
||||
import { useAvatarStore, useGalleryStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const API = inject('API');
|
||||
const { avatarDialog } = storeToRefs(useAvatarStore());
|
||||
const { previousImagesTable } = storeToRefs(useGalleryStore());
|
||||
const { applyAvatar } = useAvatarStore();
|
||||
|
||||
const props = defineProps({
|
||||
changeAvatarImageDialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
previousImagesTable: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
avatarDialog: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
previousImagesFileId: {
|
||||
type: String,
|
||||
default: ''
|
||||
@@ -121,7 +118,7 @@
|
||||
}
|
||||
};
|
||||
const files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length || !props.avatarDialog.visible || props.avatarDialog.loading) {
|
||||
if (!files.length || !avatarDialog.value.visible || avatarDialog.value.loading) {
|
||||
clearFile();
|
||||
return;
|
||||
}
|
||||
@@ -156,8 +153,8 @@
|
||||
const signatureMd5 = await genMd5(base64SignatureFile);
|
||||
const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10);
|
||||
|
||||
const avatarId = props.avatarDialog.id;
|
||||
const { imageUrl } = props.avatarDialog.ref;
|
||||
const avatarId = avatarDialog.value.id;
|
||||
const { imageUrl } = avatarDialog.value.ref;
|
||||
|
||||
const fileId = extractFileId(imageUrl);
|
||||
if (!fileId) {
|
||||
@@ -206,7 +203,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageInit(args) {
|
||||
// API.$on('AVATARIMAGE:INIT')
|
||||
const fileId = args.json.id;
|
||||
const fileVersion = args.json.versions[args.json.versions.length - 1].version;
|
||||
const params = {
|
||||
@@ -218,7 +214,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageFileStart(args) {
|
||||
// API.$on('AVATARIMAGE:FILESTART')
|
||||
const { url } = args.json;
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
@@ -242,7 +237,7 @@
|
||||
|
||||
if (json.status !== 200) {
|
||||
changeAvatarImageDialogLoading.value = false;
|
||||
API.$throw('Avatar image upload failed', json, params.url);
|
||||
$throw('Avatar image upload failed', json, params.url);
|
||||
}
|
||||
const args = {
|
||||
json,
|
||||
@@ -252,7 +247,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageFileAWS(args) {
|
||||
// API.$on('AVATARIMAGE:FILEAWS')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -263,7 +257,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageFileFinish(args) {
|
||||
// API.$on('AVATARIMAGE:FILEFINISH')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -274,7 +267,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageSigStart(args) {
|
||||
// API.$on('AVATARIMAGE:SIGSTART')
|
||||
const { url } = args.json;
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
@@ -298,7 +290,7 @@
|
||||
|
||||
if (json.status !== 200) {
|
||||
changeAvatarImageDialogLoading.value = false;
|
||||
API.$throw('Avatar image upload failed', json, params.url);
|
||||
$throw('Avatar image upload failed', json, params.url);
|
||||
}
|
||||
const args = {
|
||||
json,
|
||||
@@ -308,7 +300,6 @@
|
||||
}
|
||||
|
||||
async function avatarImageSigAWS(args) {
|
||||
// API.$on('AVATARIMAGE:SIGAWS')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -319,18 +310,17 @@
|
||||
}
|
||||
|
||||
async function avatarImageSigFinish(args) {
|
||||
// API.$on('AVATARIMAGE:SIGFINISH')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const parmas = {
|
||||
id: avatarImage.value.avatarId,
|
||||
imageUrl: `${API.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
||||
imageUrl: `${AppGlobal.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
||||
};
|
||||
const res = await imageRequest.setAvatarImage(parmas);
|
||||
return avatarImageSet(res);
|
||||
}
|
||||
|
||||
async function avatarImageSet(args) {
|
||||
// API.$on('AVATARIMAGE:SET')
|
||||
applyAvatar(args.json);
|
||||
changeAvatarImageDialogLoading.value = false;
|
||||
if (args.json.imageUrl === args.params.imageUrl) {
|
||||
$message({
|
||||
@@ -339,7 +329,7 @@
|
||||
});
|
||||
refresh();
|
||||
} else {
|
||||
API.$throw(0, 'Avatar image change failed', args.params.imageUrl);
|
||||
$throw(0, 'Avatar image change failed', args.params.imageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,42 +342,22 @@
|
||||
function setAvatarImage(image) {
|
||||
changeAvatarImageDialogLoading.value = true;
|
||||
const parmas = {
|
||||
id: props.avatarDialog.id,
|
||||
imageUrl: `${API.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
||||
id: avatarDialog.value.id,
|
||||
imageUrl: `${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
||||
};
|
||||
imageRequest.setAvatarImage(parmas).finally(() => {
|
||||
changeAvatarImageDialogLoading.value = false;
|
||||
closeDialog();
|
||||
});
|
||||
imageRequest
|
||||
.setAvatarImage(parmas)
|
||||
.then((args) => applyAvatar(args.json))
|
||||
.finally(() => {
|
||||
changeAvatarImageDialogLoading.value = false;
|
||||
closeDialog();
|
||||
});
|
||||
}
|
||||
|
||||
function compareCurrentImage(image) {
|
||||
return (
|
||||
`${API.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
||||
props.avatarDialog.ref.imageUrl
|
||||
`${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
||||
avatarDialog.value.ref.imageUrl
|
||||
);
|
||||
}
|
||||
|
||||
// $app.methods.deleteAvatarImage = function () {
|
||||
// this.changeAvatarImageDialogLoading = true;
|
||||
// var parmas = {
|
||||
// fileId: this.previousImagesFileId,
|
||||
// version: this.previousImagesTable[0].version
|
||||
// };
|
||||
// vrcPlusIconRequest
|
||||
// .deleteFileVersion(parmas)
|
||||
// .then((args) => {
|
||||
// this.previousImagesFileId = args.json.id;
|
||||
// var images = [];
|
||||
// args.json.versions.forEach((item) => {
|
||||
// if (!item.deleted) {
|
||||
// images.unshift(item);
|
||||
// }
|
||||
// });
|
||||
// this.checkPreviousImageAvailable(images);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// this.changeAvatarImageDialogLoading = false;
|
||||
// });
|
||||
// };
|
||||
</script>
|
||||
|
||||
@@ -64,13 +64,16 @@
|
||||
import { watch, getCurrentInstance } from 'vue';
|
||||
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import utils from '../../../classes/utils';
|
||||
import { arraysMatch } from '../../../shared/utils';
|
||||
import { avatarRequest } from '../../../api';
|
||||
import { useAvatarStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const { applyAvatar } = useAvatarStore();
|
||||
|
||||
const props = defineProps({
|
||||
setAvatarStylesDialog: {
|
||||
type: Object,
|
||||
@@ -125,7 +128,7 @@
|
||||
if (
|
||||
props.setAvatarStylesDialog.initialPrimaryStyle === props.setAvatarStylesDialog.primaryStyle &&
|
||||
props.setAvatarStylesDialog.initialSecondaryStyle === props.setAvatarStylesDialog.secondaryStyle &&
|
||||
utils.arraysMatch(props.setAvatarStylesDialog.initialTags, tags)
|
||||
arraysMatch(props.setAvatarStylesDialog.initialTags, tags)
|
||||
) {
|
||||
props.setAvatarStylesDialog.visible = false;
|
||||
return;
|
||||
@@ -139,7 +142,8 @@
|
||||
};
|
||||
avatarRequest
|
||||
.saveAvatar(params)
|
||||
.then(() => {
|
||||
.then((args) => {
|
||||
applyAvatar(args.json);
|
||||
$message.success(t('dialog.set_avatar_styles.save_success'));
|
||||
props.setAvatarStylesDialog.visible = false;
|
||||
})
|
||||
|
||||
@@ -94,12 +94,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, watch, getCurrentInstance } from 'vue';
|
||||
|
||||
import { getCurrentInstance, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { avatarRequest } from '../../../api';
|
||||
import { useAvatarStore } from '../../../stores';
|
||||
|
||||
const showAvatarDialog = inject('showAvatarDialog');
|
||||
const { showAvatarDialog, applyAvatar } = useAvatarStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
@@ -220,10 +220,11 @@
|
||||
tags.push(tag);
|
||||
}
|
||||
}
|
||||
await avatarRequest.saveAvatar({
|
||||
const args = await avatarRequest.saveAvatar({
|
||||
id: ref.id,
|
||||
tags
|
||||
});
|
||||
applyAvatar(args.json);
|
||||
D.selectedCount--;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -270,15 +271,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// useless
|
||||
// $app.data.avatarContentTags = [
|
||||
// 'content_horror',
|
||||
// 'content_gore',
|
||||
// 'content_violence',
|
||||
// 'content_adult',
|
||||
// 'content_sex'
|
||||
// ];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<safe-dialog ref="favoriteDialog" :visible.sync="isVisible" :title="$t('dialog.favorite.header')" width="300px">
|
||||
<safe-dialog ref="favoriteDialogRef" :visible.sync="isVisible" :title="t('dialog.favorite.header')" width="300px">
|
||||
<div v-loading="loading">
|
||||
<span style="display: block; text-align: center">{{ $t('dialog.favorite.vrchat_favorites') }}</span>
|
||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.vrchat_favorites') }}</span>
|
||||
<template v-if="favoriteDialog.currentGroup && favoriteDialog.currentGroup.key">
|
||||
<el-button
|
||||
style="display: block; width: 100%; margin: 10px 0"
|
||||
@@ -22,7 +22,7 @@
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="favoriteDialog.type === 'world'" style="margin-top: 20px">
|
||||
<span style="display: block; text-align: center">{{ $t('dialog.favorite.local_favorites') }}</span>
|
||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_favorites') }}</span>
|
||||
<template v-for="group in localWorldFavoriteGroups">
|
||||
<el-button
|
||||
v-if="hasLocalWorldFavorite(favoriteDialog.objectId, group)"
|
||||
@@ -42,7 +42,7 @@
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="favoriteDialog.type === 'avatar'" style="margin-top: 20px">
|
||||
<span style="display: block; text-align: center">{{ $t('dialog.favorite.local_avatar_favorites') }}</span>
|
||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_avatar_favorites') }}</span>
|
||||
<template v-for="group in localAvatarFavoriteGroups">
|
||||
<el-button
|
||||
v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)"
|
||||
@@ -65,122 +65,88 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { favoriteRequest } from '../../api';
|
||||
<script setup>
|
||||
import Noty from 'noty';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { favoriteRequest } from '../../api';
|
||||
import { adjustDialogZ } from '../../shared/utils';
|
||||
import { useFavoriteStore, useUserStore } from '../../stores';
|
||||
|
||||
export default {
|
||||
name: 'ChooseFavoriteGroupDialog',
|
||||
inject: ['API', 'adjustDialogZ'],
|
||||
props: {
|
||||
favoriteDialog: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
type: '',
|
||||
objectId: '',
|
||||
currentGroup: {}
|
||||
})
|
||||
},
|
||||
localWorldFavoriteGroups: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
localAvatarFavoriteGroups: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
hasLocalWorldFavorite: {
|
||||
type: Function,
|
||||
default: () => () => false
|
||||
},
|
||||
getLocalWorldFavoriteGroupLength: {
|
||||
type: Function,
|
||||
default: () => () => 0
|
||||
},
|
||||
hasLocalAvatarFavorite: {
|
||||
type: Function,
|
||||
default: () => () => false
|
||||
},
|
||||
getLocalAvatarFavoriteGroupLength: {
|
||||
type: Function,
|
||||
default: () => () => 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
groups: [],
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.favoriteDialog.visible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:favorite-dialog', { ...this.favoriteDialog, visible: value });
|
||||
}
|
||||
},
|
||||
isLocalUserVrcplusSupporter() {
|
||||
return this.API.currentUser.$isVRCPlus;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'favoriteDialog.visible'(value) {
|
||||
if (value) {
|
||||
this.initFavoriteDialog();
|
||||
this.$nextTick(() => {
|
||||
this.adjustDialogZ(this.$refs.favoriteDialog.$el);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initFavoriteDialog() {
|
||||
if (this.favoriteDialog.type === 'friend') {
|
||||
this.groups = this.API.favoriteFriendGroups;
|
||||
} else if (this.favoriteDialog.type === 'world') {
|
||||
this.groups = this.API.favoriteWorldGroups;
|
||||
} else if (this.favoriteDialog.type === 'avatar') {
|
||||
this.groups = this.API.favoriteAvatarGroups;
|
||||
}
|
||||
},
|
||||
addFavorite(group) {
|
||||
const D = this.favoriteDialog;
|
||||
this.loading = true;
|
||||
favoriteRequest
|
||||
.addFavorite({
|
||||
type: D.type,
|
||||
favoriteId: D.objectId,
|
||||
tags: group.name
|
||||
})
|
||||
.then(() => {
|
||||
this.isVisible = false;
|
||||
new Noty({
|
||||
type: 'success',
|
||||
text: 'Favorite added'
|
||||
}).show();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
addLocalWorldFavorite(...args) {
|
||||
this.$emit('addLocalWorldFavorite', ...args);
|
||||
},
|
||||
removeLocalWorldFavorite(...args) {
|
||||
this.$emit('removeLocalWorldFavorite', ...args);
|
||||
},
|
||||
addLocalAvatarFavorite(...args) {
|
||||
this.$emit('addLocalAvatarFavorite', ...args);
|
||||
},
|
||||
removeLocalAvatarFavorite(...args) {
|
||||
this.$emit('removeLocalAvatarFavorite', ...args);
|
||||
},
|
||||
deleteFavoriteNoConfirm(...args) {
|
||||
this.$emit('deleteFavoriteNoConfirm', ...args);
|
||||
const { t } = useI18n();
|
||||
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const {
|
||||
favoriteFriendGroups,
|
||||
favoriteAvatarGroups,
|
||||
favoriteWorldGroups,
|
||||
localAvatarFavoriteGroups,
|
||||
favoriteDialog,
|
||||
localWorldFavoriteGroups
|
||||
} = storeToRefs(favoriteStore);
|
||||
const {
|
||||
getLocalWorldFavoriteGroupLength,
|
||||
addLocalWorldFavorite,
|
||||
hasLocalWorldFavorite,
|
||||
hasLocalAvatarFavorite,
|
||||
addLocalAvatarFavorite,
|
||||
getLocalAvatarFavoriteGroupLength,
|
||||
removeLocalAvatarFavorite,
|
||||
removeLocalWorldFavorite,
|
||||
deleteFavoriteNoConfirm
|
||||
} = favoriteStore;
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const favoriteDialogRef = ref(null);
|
||||
const groups = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const isVisible = computed({
|
||||
get: () => favoriteDialog.value.visible,
|
||||
set: (v) => {
|
||||
favoriteDialog.value.visible = v;
|
||||
}
|
||||
});
|
||||
|
||||
const isLocalUserVrcplusSupporter = computed(() => currentUser.value.$isVRCPlus);
|
||||
|
||||
watch(
|
||||
() => favoriteDialog.value.visible,
|
||||
async (value) => {
|
||||
if (value) {
|
||||
initFavoriteDialog();
|
||||
await nextTick();
|
||||
adjustDialogZ(favoriteDialogRef.value.$el);
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function initFavoriteDialog() {
|
||||
if (favoriteDialog.value.type === 'friend') {
|
||||
groups.value = favoriteFriendGroups.value;
|
||||
} else if (favoriteDialog.value.type === 'world') {
|
||||
groups.value = favoriteWorldGroups.value;
|
||||
} else if (favoriteDialog.value.type === 'avatar') {
|
||||
groups.value = favoriteAvatarGroups.value;
|
||||
}
|
||||
}
|
||||
|
||||
function addFavorite(group) {
|
||||
const D = favoriteDialog.value;
|
||||
loading.value = true;
|
||||
favoriteRequest
|
||||
.addFavorite({
|
||||
type: D.type,
|
||||
favoriteId: D.objectId,
|
||||
tags: group.name
|
||||
})
|
||||
.then(() => {
|
||||
isVisible.value = false;
|
||||
new Noty({ type: 'success', text: 'favorite added!' }).show();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
ref="fullscreenImageDialog"
|
||||
class="x-dialog"
|
||||
:visible.sync="fullscreenImageDialog.visible"
|
||||
append-to-body
|
||||
top="1vh"
|
||||
width="97vw">
|
||||
<safe-dialog class="x-dialog" :visible.sync="fullscreenImageDialog.visible" append-to-body top="1vh" width="97vw">
|
||||
<div>
|
||||
<div style="margin: 0 0 5px 5px">
|
||||
<el-button
|
||||
@@ -29,21 +23,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import utils from '../../classes/utils';
|
||||
import { copyToClipboard, extractFileId } from '../../composables/shared/utils';
|
||||
import webApiService from '../../service/webapi';
|
||||
import Noty from 'noty';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { copyToClipboard, escapeTag, extractFileId } from '../../shared/utils';
|
||||
import { useGalleryStore } from '../../stores';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { $message } = proxy;
|
||||
|
||||
defineProps({
|
||||
fullscreenImageDialog: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
||||
|
||||
function copyImageUrl(imageUrl) {
|
||||
copyToClipboard(imageUrl, 'ImageUrl copied to clipboard');
|
||||
@@ -84,7 +73,7 @@
|
||||
} catch {
|
||||
new Noty({
|
||||
type: 'error',
|
||||
text: utils.escapeTag(`Failed to download image. ${url}`)
|
||||
text: escapeTag(`Failed to download image. ${url}`)
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
size="small"
|
||||
@click="displayGalleryUpload"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus">
|
||||
:disabled="!currentUser.$isVRCPlus">
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
@@ -41,7 +41,7 @@
|
||||
size="small"
|
||||
@click="setProfilePicOverride('')"
|
||||
icon="el-icon-close"
|
||||
:disabled="!API.currentUser.profilePicOverride">
|
||||
:disabled="!currentUser.profilePicOverride">
|
||||
{{ t('dialog.gallery_icons.clear') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
@@ -102,7 +102,7 @@
|
||||
size="small"
|
||||
@click="displayVRCPlusIconUpload"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus">
|
||||
:disabled="!currentUser.$isVRCPlus">
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
@@ -110,7 +110,7 @@
|
||||
size="small"
|
||||
@click="setVRCPlusIcon('')"
|
||||
icon="el-icon-close"
|
||||
:disabled="!API.currentUser.userIcon">
|
||||
:disabled="!currentUser.userIcon">
|
||||
{{ t('dialog.gallery_icons.clear') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
@@ -150,7 +150,7 @@
|
||||
<span slot="label">
|
||||
{{ t('dialog.gallery_icons.emojis') }}
|
||||
<span style="color: #909399; font-size: 12px; margin-left: 5px">
|
||||
{{ emojiTable.length }}/{{ API.cachedConfig?.maxUserEmoji }}
|
||||
{{ emojiTable.length }}/{{ cachedConfig?.maxUserEmoji }}
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
@@ -172,7 +172,7 @@
|
||||
size="small"
|
||||
@click="displayEmojiUpload"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus">
|
||||
:disabled="!currentUser.$isVRCPlus">
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
@@ -237,7 +237,7 @@
|
||||
@click="
|
||||
showFullscreenImageDialog(
|
||||
image.versions[image.versions.length - 1].file.url,
|
||||
getEmojiName(image)
|
||||
getEmojiFileName(image)
|
||||
)
|
||||
">
|
||||
<template v-if="image.frames">
|
||||
@@ -271,7 +271,7 @@
|
||||
@click="
|
||||
showFullscreenImageDialog(
|
||||
image.versions[image.versions.length - 1].file.url,
|
||||
getEmojiName(image)
|
||||
getEmojiFileName(image)
|
||||
)
|
||||
"
|
||||
size="mini"
|
||||
@@ -292,7 +292,7 @@
|
||||
<span slot="label">
|
||||
{{ t('dialog.gallery_icons.stickers') }}
|
||||
<span style="color: #909399; font-size: 12px; margin-left: 5px">
|
||||
{{ stickerTable.length }}/{{ API.cachedConfig?.maxUserStickers }}
|
||||
{{ stickerTable.length }}/{{ cachedConfig?.maxUserStickers }}
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
@@ -313,7 +313,7 @@
|
||||
size="small"
|
||||
@click="displayStickerUpload"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus">
|
||||
:disabled="!currentUser.$isVRCPlus">
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
@@ -373,7 +373,7 @@
|
||||
size="small"
|
||||
@click="displayPrintUpload"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus">
|
||||
:disabled="!currentUser.$isVRCPlus">
|
||||
{{ t('dialog.gallery_icons.upload') }}
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
@@ -405,25 +405,25 @@
|
||||
<div style="margin-top: 5px; width: 208px">
|
||||
<span class="x-ellipsis" v-if="image.note" v-text="image.note" style="display: block"></span>
|
||||
<span v-else style="display: block"> </span>
|
||||
<location
|
||||
<Location
|
||||
class="x-ellipsis"
|
||||
v-if="image.worldId"
|
||||
:location="image.worldId"
|
||||
:hint="image.worldName"
|
||||
style="display: block"></location>
|
||||
style="display: block" />
|
||||
<span v-else style="display: block"> </span>
|
||||
<display-name
|
||||
<DisplayName
|
||||
class="x-ellipsis"
|
||||
v-if="image.authorId"
|
||||
:userid="image.authorId"
|
||||
:hint="image.authorName"
|
||||
style="color: #909399; font-family: monospace; display: block"></display-name>
|
||||
style="color: #909399; font-family: monospace; display: block" />
|
||||
<span v-else style="font-family: monospace; display: block"> </span>
|
||||
<span
|
||||
class="x-ellipsis"
|
||||
v-if="image.createdAt"
|
||||
style="color: #909399; font-family: monospace; font-size: 11px; display: block">
|
||||
{{ image.createdAt | formatDate('long') }}
|
||||
{{ formatDateFilter(image.createdAt, 'long') }}
|
||||
</span>
|
||||
<span v-else style="display: block"> </span>
|
||||
</div>
|
||||
@@ -479,7 +479,7 @@
|
||||
<span
|
||||
class="x-ellipsis"
|
||||
style="color: #909399; font-family: monospace; font-size: 11px; display: block">
|
||||
{{ item.created_at | formatDate('long') }}
|
||||
{{ formatDateFilter(item.created_at, 'long') }}
|
||||
</span>
|
||||
<span v-text="item.itemType" style="display: block"></span>
|
||||
</div>
|
||||
@@ -500,95 +500,50 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { inventoryRequest, miscRequest, userRequest, vrcPlusIconRequest, vrcPlusImageRequest } from '../../api';
|
||||
import { extractFileId } from '../../composables/shared/utils';
|
||||
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../composables/user/constants/emoji';
|
||||
import { getPrintFileName, getEmojiFileName } from '../../composables/user/utils';
|
||||
import Location from '../Location.vue';
|
||||
import { AppGlobal } from '../../service/appConfig';
|
||||
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../shared/constants';
|
||||
import { extractFileId, formatDateFilter, getEmojiFileName, getPrintFileName } from '../../shared/utils';
|
||||
import { useAdvancedSettingsStore, useAuthStore, useGalleryStore, useUserStore } from '../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { $message } = proxy;
|
||||
const {
|
||||
galleryTable,
|
||||
galleryDialogVisible,
|
||||
galleryDialogGalleryLoading,
|
||||
galleryDialogIconsLoading,
|
||||
galleryDialogEmojisLoading,
|
||||
galleryDialogStickersLoading,
|
||||
galleryDialogPrintsLoading,
|
||||
galleryDialogInventoryLoading,
|
||||
VRCPlusIconsTable,
|
||||
printUploadNote,
|
||||
printCropBorder,
|
||||
stickerTable,
|
||||
printTable,
|
||||
emojiTable,
|
||||
inventoryTable
|
||||
} = storeToRefs(useGalleryStore());
|
||||
const {
|
||||
refreshGalleryTable,
|
||||
refreshVRCPlusIconsTable,
|
||||
refreshStickerTable,
|
||||
refreshPrintTable,
|
||||
refreshEmojiTable,
|
||||
getInventory,
|
||||
handleStickerAdd,
|
||||
handleGalleryImageAdd
|
||||
} = useGalleryStore();
|
||||
|
||||
const API = inject('API');
|
||||
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
|
||||
|
||||
const props = defineProps({
|
||||
galleryDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogGalleryLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogIconsLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogEmojisLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogStickersLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogPrintsLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryDialogInventoryLoading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
galleryTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
VRCPlusIconsTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
emojiTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
stickerTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
printUploadNote: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
printCropBorder: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
printTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
inventoryTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
'refreshGalleryTable',
|
||||
'refreshVRCPlusIconsTable',
|
||||
'refreshStickerTable',
|
||||
'refreshEmojiTable',
|
||||
'refreshPrintTable',
|
||||
'getInventory',
|
||||
'closeGalleryDialog'
|
||||
]);
|
||||
const { currentUserInventory } = storeToRefs(useAdvancedSettingsStore());
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { cachedConfig } = storeToRefs(useAuthStore());
|
||||
|
||||
const emojiAnimFps = ref(15);
|
||||
const emojiAnimFrameCount = ref(4);
|
||||
@@ -596,12 +551,8 @@
|
||||
const emojiAnimationStyle = ref('Stop');
|
||||
const emojiAnimLoopPingPong = ref(false);
|
||||
|
||||
function refreshGalleryTable() {
|
||||
emit('refreshGalleryTable');
|
||||
}
|
||||
|
||||
function closeGalleryDialog() {
|
||||
emit('closeGalleryDialog');
|
||||
galleryDialogVisible.value = false;
|
||||
}
|
||||
|
||||
function onFileChangeGallery(e) {
|
||||
@@ -616,7 +567,7 @@
|
||||
}
|
||||
if (files[0].size >= 100000000) {
|
||||
// 100MB
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.too_large'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -624,7 +575,7 @@
|
||||
return;
|
||||
}
|
||||
if (!files[0].type.match(/image.*/)) {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.not_image'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -635,7 +586,8 @@
|
||||
r.onload = function () {
|
||||
const base64Body = btoa(r.result);
|
||||
vrcPlusImageRequest.uploadGalleryImage(base64Body).then((args) => {
|
||||
$message({
|
||||
handleGalleryImageAdd(args);
|
||||
proxy.$message({
|
||||
message: t('message.gallery.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
@@ -651,8 +603,8 @@
|
||||
}
|
||||
|
||||
function setProfilePicOverride(fileId) {
|
||||
if (!API.currentUser.$isVRCPlus) {
|
||||
$message({
|
||||
if (!currentUser.value.$isVRCPlus) {
|
||||
proxy.$message({
|
||||
message: 'VRCPlus required',
|
||||
type: 'error'
|
||||
});
|
||||
@@ -660,9 +612,9 @@
|
||||
}
|
||||
let profilePicOverride = '';
|
||||
if (fileId) {
|
||||
profilePicOverride = `${API.endpointDomain}/file/${fileId}/1`;
|
||||
profilePicOverride = `${AppGlobal.endpointDomain}/file/${fileId}/1`;
|
||||
}
|
||||
if (profilePicOverride === API.currentUser.profilePicOverride) {
|
||||
if (profilePicOverride === currentUser.value.profilePicOverride) {
|
||||
return;
|
||||
}
|
||||
userRequest
|
||||
@@ -670,7 +622,7 @@
|
||||
profilePicOverride
|
||||
})
|
||||
.then((args) => {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: 'Profile picture changed',
|
||||
type: 'success'
|
||||
});
|
||||
@@ -679,7 +631,7 @@
|
||||
}
|
||||
|
||||
function compareCurrentProfilePic(fileId) {
|
||||
const currentProfilePicOverride = extractFileId(API.currentUser.profilePicOverride);
|
||||
const currentProfilePicOverride = extractFileId(currentUser.value.profilePicOverride);
|
||||
if (fileId === currentProfilePicOverride) {
|
||||
return true;
|
||||
}
|
||||
@@ -688,9 +640,7 @@
|
||||
|
||||
function deleteGalleryImage(fileId) {
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
// API.$emit('GALLERYIMAGE:DELETE', args);
|
||||
// API.$on('GALLERYIMAGE:DELETE')
|
||||
const array = props.galleryTable;
|
||||
const array = galleryTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (args.fileId === array[i].id) {
|
||||
@@ -715,7 +665,7 @@
|
||||
}
|
||||
if (files[0].size >= 100000000) {
|
||||
// 100MB
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.too_large'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -723,7 +673,7 @@
|
||||
return;
|
||||
}
|
||||
if (!files[0].type.match(/image.*/)) {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.not_image'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -734,7 +684,10 @@
|
||||
r.onload = function () {
|
||||
const base64Body = btoa(r.result);
|
||||
vrcPlusIconRequest.uploadVRCPlusIcon(base64Body).then((args) => {
|
||||
$message({
|
||||
if (Object.keys(VRCPlusIconsTable.value).length !== 0) {
|
||||
VRCPlusIconsTable.value.unshift(args.json);
|
||||
}
|
||||
proxy.$message({
|
||||
message: t('message.icon.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
@@ -745,17 +698,13 @@
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function refreshVRCPlusIconsTable() {
|
||||
emit('refreshVRCPlusIconsTable');
|
||||
}
|
||||
|
||||
function displayVRCPlusIconUpload() {
|
||||
document.getElementById('VRCPlusIconUploadButton').click();
|
||||
}
|
||||
|
||||
function setVRCPlusIcon(fileId) {
|
||||
if (!API.currentUser.$isVRCPlus) {
|
||||
$message({
|
||||
if (!currentUser.value.$isVRCPlus) {
|
||||
proxy.$message({
|
||||
message: 'VRCPlus required',
|
||||
type: 'error'
|
||||
});
|
||||
@@ -763,9 +712,9 @@
|
||||
}
|
||||
let userIcon = '';
|
||||
if (fileId) {
|
||||
userIcon = `${API.endpointDomain}/file/${fileId}/1`;
|
||||
userIcon = `${AppGlobal.endpointDomain}/file/${fileId}/1`;
|
||||
}
|
||||
if (userIcon === API.currentUser.userIcon) {
|
||||
if (userIcon === currentUser.value.userIcon) {
|
||||
return;
|
||||
}
|
||||
userRequest
|
||||
@@ -773,7 +722,7 @@
|
||||
userIcon
|
||||
})
|
||||
.then((args) => {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: 'Icon changed',
|
||||
type: 'success'
|
||||
});
|
||||
@@ -782,7 +731,7 @@
|
||||
}
|
||||
|
||||
function compareCurrentVRCPlusIcon(userIcon) {
|
||||
const currentUserIcon = extractFileId(API.currentUser.userIcon);
|
||||
const currentUserIcon = extractFileId(currentUser.value.userIcon);
|
||||
if (userIcon === currentUserIcon) {
|
||||
return true;
|
||||
}
|
||||
@@ -791,9 +740,7 @@
|
||||
|
||||
function deleteVRCPlusIcon(fileId) {
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
// API.$emit('VRCPLUSICON:DELETE', args);
|
||||
// API.$on('VRCPLUSICON:DELETE')
|
||||
const array = props.VRCPlusIconsTable;
|
||||
const array = VRCPlusIconsTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (args.fileId === array[i].id) {
|
||||
@@ -823,7 +770,7 @@
|
||||
emojiAnimFps.value = parseInt(value.replace('fps', ''));
|
||||
}
|
||||
if (value.endsWith('loopStyle')) {
|
||||
emojiAnimLoopPingPong.value = value.replace('loopStyle', '').toLowerCase() === 'pingpong';
|
||||
emojiAnimLoopPingPong.value = value.replace('loopStyle', '').toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -840,7 +787,7 @@
|
||||
}
|
||||
if (files[0].size >= 100000000) {
|
||||
// 100MB
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.too_large'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -848,7 +795,7 @@
|
||||
return;
|
||||
}
|
||||
if (!files[0].type.match(/image.*/)) {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.not_image'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -873,7 +820,10 @@
|
||||
}
|
||||
const base64Body = btoa(r.result);
|
||||
vrcPlusImageRequest.uploadEmoji(base64Body, params).then((args) => {
|
||||
$message({
|
||||
if (Object.keys(emojiTable).length !== 0) {
|
||||
emojiTable.unshift(args.json);
|
||||
}
|
||||
proxy.$message({
|
||||
message: t('message.emoji.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
@@ -884,18 +834,10 @@
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function refreshEmojiTable() {
|
||||
emit('refreshEmojiTable');
|
||||
}
|
||||
|
||||
function displayEmojiUpload() {
|
||||
document.getElementById('EmojiUploadButton').click();
|
||||
}
|
||||
|
||||
function getEmojiName(emoji) {
|
||||
getEmojiFileName(emoji);
|
||||
}
|
||||
|
||||
function generateEmojiStyle(url, fps, frameCount, loopStyle) {
|
||||
let framesPerLine = 2;
|
||||
if (frameCount > 4) framesPerLine = 4;
|
||||
@@ -917,9 +859,7 @@
|
||||
|
||||
function deleteEmoji(fileId) {
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
// API.$emit('EMOJI:DELETE', args);
|
||||
// API.$on('EMOJI:DELETE')
|
||||
const array = props.emojiTable;
|
||||
const array = emojiTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (args.fileId === array[i].id) {
|
||||
@@ -943,7 +883,7 @@
|
||||
}
|
||||
if (files[0].size >= 100000000) {
|
||||
// 100MB
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.too_large'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -951,7 +891,7 @@
|
||||
return;
|
||||
}
|
||||
if (!files[0].type.match(/image.*/)) {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.not_image'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -966,7 +906,8 @@
|
||||
};
|
||||
const base64Body = btoa(r.result);
|
||||
vrcPlusImageRequest.uploadSticker(base64Body, params).then((args) => {
|
||||
$message({
|
||||
handleStickerAdd(args);
|
||||
proxy.$message({
|
||||
message: t('message.sticker.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
@@ -977,19 +918,13 @@
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function refreshStickerTable() {
|
||||
emit('refreshStickerTable');
|
||||
}
|
||||
|
||||
function displayStickerUpload() {
|
||||
document.getElementById('StickerUploadButton').click();
|
||||
}
|
||||
|
||||
function deleteSticker(fileId) {
|
||||
miscRequest.deleteFile(fileId).then((args) => {
|
||||
// API.$emit('STICKER:DELETE', args);
|
||||
// API.$on('STICKER:DELETE')
|
||||
const array = props.stickerTable;
|
||||
const array = stickerTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (args.fileId === array[i].id) {
|
||||
@@ -1014,7 +949,7 @@
|
||||
}
|
||||
if (files[0].size >= 100000000) {
|
||||
// 100MB
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.too_large'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -1022,7 +957,7 @@
|
||||
return;
|
||||
}
|
||||
if (!files[0].type.match(/image.*/)) {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.file.not_image'),
|
||||
type: 'error'
|
||||
});
|
||||
@@ -1036,20 +971,19 @@
|
||||
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||
const timestamp = date.toISOString().slice(0, 19);
|
||||
const params = {
|
||||
note: props.printUploadNote,
|
||||
note: printUploadNote.value,
|
||||
// worldId: '',
|
||||
timestamp
|
||||
};
|
||||
const base64Body = btoa(r.result);
|
||||
const cropWhiteBorder = props.printCropBorder;
|
||||
const cropWhiteBorder = printCropBorder.value;
|
||||
vrcPlusImageRequest.uploadPrint(base64Body, cropWhiteBorder, params).then((args) => {
|
||||
$message({
|
||||
proxy.$message({
|
||||
message: t('message.print.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
// API.$on('PRINT:ADD')
|
||||
if (Object.keys(props.printTable).length !== 0) {
|
||||
props.printTable.unshift(args.json);
|
||||
if (Object.keys(printTable.value).length !== 0) {
|
||||
printTable.value.unshift(args.json);
|
||||
}
|
||||
|
||||
return args;
|
||||
@@ -1059,22 +993,13 @@
|
||||
clearFile();
|
||||
}
|
||||
|
||||
function refreshPrintTable() {
|
||||
emit('refreshPrintTable');
|
||||
}
|
||||
|
||||
function getInventory() {
|
||||
emit('getInventory');
|
||||
}
|
||||
|
||||
function displayPrintUpload() {
|
||||
document.getElementById('PrintUploadButton').click();
|
||||
}
|
||||
|
||||
function deletePrint(printId) {
|
||||
vrcPlusImageRequest.deletePrint(printId).then((args) => {
|
||||
// API.$on('PRINT:DELETE');
|
||||
const array = props.printTable;
|
||||
const array = printTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (args.printId === array[i].id) {
|
||||
@@ -1090,8 +1015,8 @@
|
||||
const args = await inventoryRequest.consumeInventoryBundle({
|
||||
inventoryId
|
||||
});
|
||||
API.currentUserInventory.delete(inventoryId);
|
||||
const array = props.inventoryTable;
|
||||
currentUserInventory.value.delete(inventoryId);
|
||||
const array = inventoryTable.value;
|
||||
const { length } = array;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (inventoryId === array[i].id) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
type="default"
|
||||
size="small"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="!API.currentUser.$isVRCPlus"
|
||||
:disabled="!currentUser.$isVRCPlus"
|
||||
@click="displayGalleryUpload"
|
||||
>{{ t('dialog.gallery_select.upload') }}</el-button
|
||||
>
|
||||
@@ -50,30 +50,27 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, getCurrentInstance } from 'vue';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { vrcPlusImageRequest } from '../../../api';
|
||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { $message } = proxy;
|
||||
|
||||
const API = inject('API');
|
||||
const { galleryTable } = storeToRefs(useGalleryStore());
|
||||
const { refreshGalleryTable, handleGalleryImageAdd } = useGalleryStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
gallerySelectDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
galleryTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['refreshGalleryTable']);
|
||||
|
||||
function selectImageGallerySelect(imageUrl, fileId) {
|
||||
const D = props.gallerySelectDialog;
|
||||
D.selectedFileId = fileId;
|
||||
@@ -116,13 +113,13 @@
|
||||
r.onload = function () {
|
||||
const base64Body = btoa(r.result);
|
||||
vrcPlusImageRequest.uploadGalleryImage(base64Body).then((args) => {
|
||||
handleGalleryImageAdd(args);
|
||||
$message({
|
||||
message: t('message.gallery.uploaded'),
|
||||
type: 'success'
|
||||
});
|
||||
// API.$on('GALLERYIMAGE:ADD')
|
||||
if (Object.keys(props.galleryTable).length !== 0) {
|
||||
props.galleryTable.unshift(args.json);
|
||||
if (Object.keys(galleryTable.value).length !== 0) {
|
||||
galleryTable.value.unshift(args.json);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
@@ -130,7 +127,4 @@
|
||||
r.readAsBinaryString(files[0]);
|
||||
clearFile();
|
||||
}
|
||||
function refreshGalleryTable() {
|
||||
emit('refreshGalleryTable');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</el-popover>
|
||||
<div style="flex: 1; display: flex; align-items: center; margin-left: 15px">
|
||||
<div class="group-header" style="flex: 1">
|
||||
<span v-if="groupDialog.ref.ownerId === API.currentUser.id" style="margin-right: 5px">👑</span>
|
||||
<span v-if="groupDialog.ref.ownerId === currentUser.id" style="margin-right: 5px">👑</span>
|
||||
<span class="dialog-title" style="margin-right: 5px" v-text="groupDialog.ref.name"></span>
|
||||
<span
|
||||
class="group-discriminator x-grey"
|
||||
@@ -399,9 +399,9 @@
|
||||
</span>
|
||||
<div v-for="room in groupDialog.instances" :key="room.tag" style="width: 100%">
|
||||
<div style="margin: 5px 0">
|
||||
<location :location="room.tag" style="display: inline-block" />
|
||||
<Location :location="room.tag" style="display: inline-block" />
|
||||
<el-tooltip placement="top" content="Invite yourself" :disabled="hideTooltips">
|
||||
<invite-yourself :location="room.tag" style="margin-left: 5px" />
|
||||
<InviteYourself :location="room.tag" style="margin-left: 5px" />
|
||||
</el-tooltip>
|
||||
<el-tooltip placement="top" content="Refresh player count" :disabled="hideTooltips">
|
||||
<el-button
|
||||
@@ -411,12 +411,11 @@
|
||||
circle
|
||||
@click="refreshInstancePlayerCount(room.tag)" />
|
||||
</el-tooltip>
|
||||
<last-join :location="room.tag" :currentlocation="lastLocation.location" />
|
||||
<instance-info
|
||||
<LastJoin :location="room.tag" :currentlocation="lastLocation.location" />
|
||||
<InstanceInfo
|
||||
:location="room.tag"
|
||||
:instance="room.ref"
|
||||
:friendcount="room.friendCount"
|
||||
:updateelement="updateInstanceInfo" />
|
||||
:friendcount="room.friendCount" />
|
||||
</div>
|
||||
<div
|
||||
v-if="room.users.length"
|
||||
@@ -437,10 +436,10 @@
|
||||
v-text="user.displayName" />
|
||||
<span v-if="user.location === 'traveling'" class="extra">
|
||||
<i class="el-icon-loading" style="margin-right: 5px" />
|
||||
<timer :epoch="user.$travelingToTime" />
|
||||
<Timer :epoch="user.$travelingToTime" />
|
||||
</span>
|
||||
<span v-else class="extra">
|
||||
<timer :epoch="user.$location_at" />
|
||||
<Timer :epoch="user.$location_at" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -494,13 +493,14 @@
|
||||
<span>{{ t('dialog.group.posts.visibility') }}</span>
|
||||
<br />
|
||||
<template v-for="roleId in groupDialog.announcement.roleIds">
|
||||
<template v-for="role in groupDialog.ref.roles"
|
||||
><span
|
||||
v-if="role.id === roleId"
|
||||
:key="roleId + role.id"
|
||||
v-text="role.name"
|
||||
/></template>
|
||||
<span
|
||||
v-for="(role, rIndex) in groupDialog.ref.roles"
|
||||
v-if="role.id === roleId"
|
||||
:key="rIndex"
|
||||
v-text="role.name" />
|
||||
<span
|
||||
:key="rIndex"
|
||||
:key="roleId"
|
||||
v-if="
|
||||
groupDialog.announcement.roleIds.indexOf(roleId) <
|
||||
groupDialog.announcement.roleIds.length - 1
|
||||
@@ -511,18 +511,18 @@
|
||||
</template>
|
||||
<i class="el-icon-view" style="margin-right: 5px" />
|
||||
</el-tooltip>
|
||||
<display-name
|
||||
<DisplayName
|
||||
:userid="groupDialog.announcement.authorId"
|
||||
style="margin-right: 5px" />
|
||||
<span v-if="groupDialog.announcement.editorId" style="margin-right: 5px">
|
||||
({{ t('dialog.group.posts.edited_by') }}
|
||||
<display-name :userid="groupDialog.announcement.editorId" />)
|
||||
<DisplayName :userid="groupDialog.announcement.editorId" />)
|
||||
</span>
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
<span
|
||||
>{{ t('dialog.group.posts.created_at') }}
|
||||
{{ groupDialog.announcement.createdAt | formatDate('long') }}</span
|
||||
{{ formatDateFilter(groupDialog.announcement.createdAt, 'long') }}</span
|
||||
>
|
||||
<template
|
||||
v-if="
|
||||
@@ -532,11 +532,13 @@
|
||||
<br />
|
||||
<span
|
||||
>{{ t('dialog.group.posts.edited_at') }}
|
||||
{{ groupDialog.announcement.updatedAt | formatDate('long') }}</span
|
||||
{{
|
||||
formatDateFilter(groupDialog.announcement.updatedAt, 'long')
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
<timer :epoch="Date.parse(groupDialog.announcement.updatedAt)" />
|
||||
<Timer :epoch="Date.parse(groupDialog.announcement.updatedAt)" />
|
||||
</el-tooltip>
|
||||
<template v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')">
|
||||
<el-tooltip
|
||||
@@ -593,7 +595,7 @@
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('dialog.group.info.created_at') }}</span>
|
||||
<span class="extra">{{ groupDialog.ref.createdAt | formatDate('long') }}</span>
|
||||
<span class="extra">{{ formatDateFilter(groupDialog.ref.createdAt, 'long') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="cursor: default">
|
||||
@@ -667,7 +669,7 @@
|
||||
<div class="detail">
|
||||
<span class="name">{{ t('dialog.group.info.joined_at') }}</span>
|
||||
<span class="extra">{{
|
||||
groupDialog.ref.myMember.joinedAt | formatDate('long')
|
||||
formatDateFilter(groupDialog.ref.myMember.joinedAt, 'long')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -688,18 +690,18 @@
|
||||
<br />
|
||||
<span v-if="role.updatedAt"
|
||||
>{{ t('dialog.group.info.role_updated_at') }}
|
||||
{{ role.updatedAt | formatDate('long') }}</span
|
||||
{{ formatDateFilter(role.updatedAt, 'long') }}</span
|
||||
>
|
||||
<span v-else
|
||||
>{{ t('dialog.group.info.role_created_at') }}
|
||||
{{ role.createdAt | formatDate('long') }}</span
|
||||
{{ formatDateFilter(role.createdAt, 'long') }}</span
|
||||
>
|
||||
<br />
|
||||
<span>{{ t('dialog.group.info.role_permissions') }}</span>
|
||||
<br />
|
||||
<template v-for="(permission, pIndex) in role.permissions">
|
||||
<span :key="pIndex">{{ permission }}</span>
|
||||
<br />
|
||||
<br :key="pIndex + permission" />
|
||||
</template>
|
||||
</template>
|
||||
<span
|
||||
@@ -776,38 +778,40 @@
|
||||
<span>{{ t('dialog.group.posts.visibility') }}</span>
|
||||
<br />
|
||||
<template v-for="roleId in post.roleIds">
|
||||
<span
|
||||
v-for="(role, rIndex) in groupDialog.ref.roles"
|
||||
v-if="role.id === roleId"
|
||||
:key="rIndex"
|
||||
v-text="role.name" />
|
||||
<span v-if="post.roleIds.indexOf(roleId) < post.roleIds.length - 1"
|
||||
>, </span
|
||||
<template v-for="role in groupDialog.ref.roles"
|
||||
><span
|
||||
v-if="role.id === roleId"
|
||||
:key="role.id + roleId"
|
||||
v-text="role.name" />
|
||||
</template>
|
||||
<template
|
||||
v-if="post.roleIds.indexOf(roleId) < post.roleIds.length - 1"
|
||||
><span :key="roleId">, </span></template
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
<i class="el-icon-view" style="margin-right: 5px" />
|
||||
</el-tooltip>
|
||||
<display-name :userid="post.authorId" style="margin-right: 5px" />
|
||||
<DisplayName :userid="post.authorId" style="margin-right: 5px" />
|
||||
<span v-if="post.editorId" style="margin-right: 5px"
|
||||
>({{ t('dialog.group.posts.edited_by') }}
|
||||
<display-name :userid="post.editorId" />)</span
|
||||
<DisplayName :userid="post.editorId" />)</span
|
||||
>
|
||||
<el-tooltip placement="bottom">
|
||||
<template slot="content">
|
||||
<span
|
||||
>{{ t('dialog.group.posts.created_at') }}
|
||||
{{ post.createdAt | formatDate('long') }}</span
|
||||
{{ formatDateFilter(post.createdAt, 'long') }}</span
|
||||
>
|
||||
<template v-if="post.updatedAt !== post.createdAt">
|
||||
<br />
|
||||
<span
|
||||
>{{ t('dialog.group.posts.edited_at') }}
|
||||
{{ post.updatedAt | formatDate('long') }}</span
|
||||
{{ formatDateFilter(post.updatedAt, 'long') }}</span
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
<timer :epoch="Date.parse(post.updatedAt)" />
|
||||
<Timer :epoch="Date.parse(post.updatedAt)" />
|
||||
</el-tooltip>
|
||||
<template
|
||||
v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')">
|
||||
@@ -885,7 +889,7 @@
|
||||
@click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span
|
||||
>{{ groupDialog.memberSortOrder.name }}
|
||||
>{{ t(groupDialog.memberSortOrder.name) }}
|
||||
<i class="el-icon-arrow-down el-icon--right"
|
||||
/></span>
|
||||
</el-button>
|
||||
@@ -894,7 +898,7 @@
|
||||
v-for="item in groupDialogSortingOptions"
|
||||
:key="item.name"
|
||||
@click.native="setGroupMemberSortOrder(item)"
|
||||
v-text="item.name" />
|
||||
v-text="t(item.name)" />
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<span style="margin-right: 5px">{{ t('dialog.group.members.filter') }}</span>
|
||||
@@ -906,7 +910,7 @@
|
||||
@click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span
|
||||
>{{ groupDialog.memberFilter.name }}
|
||||
>{{ t(groupDialog.memberFilter.name) }}
|
||||
<i class="el-icon-arrow-down el-icon--right"
|
||||
/></span>
|
||||
</el-button>
|
||||
@@ -915,7 +919,7 @@
|
||||
v-for="item in groupDialogFilterOptions"
|
||||
:key="item.name"
|
||||
@click.native="setGroupMemberFilter(item)"
|
||||
v-text="item.name" />
|
||||
v-text="t(item.name)" />
|
||||
<el-dropdown-item
|
||||
v-for="item in groupDialog.ref.roles"
|
||||
v-if="!item.defaultRole"
|
||||
@@ -984,13 +988,13 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-for="roleId in user.roleIds">
|
||||
<span
|
||||
v-for="(role, rIndex) in groupDialog.ref.roles"
|
||||
v-if="role.id === roleId"
|
||||
:key="rIndex"
|
||||
v-text="role.name" />
|
||||
<span v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
||||
>, </span
|
||||
<template v-for="role in groupDialog.ref.roles"
|
||||
><span
|
||||
v-if="role.id === roleId"
|
||||
:key="role.id + roleId"
|
||||
v-text="role.name" /></template
|
||||
><template v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
||||
><span :key="roleId">, </span></template
|
||||
>
|
||||
</template>
|
||||
</span>
|
||||
@@ -1048,19 +1052,18 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-for="roleId in user.roleIds">
|
||||
<span
|
||||
v-for="(role, rIndex) in groupDialog.ref.roles"
|
||||
v-if="role.id === roleId"
|
||||
:key="rIndex"
|
||||
v-text="role.name" />
|
||||
<span v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
||||
>, </span
|
||||
<template v-for="role in groupDialog.ref.roles"
|
||||
><span
|
||||
v-if="role.id === roleId"
|
||||
:key="roleId + role"
|
||||
v-text="role.name" /></template
|
||||
><template v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
||||
><span :key="roleId"> </span></template
|
||||
>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<!--FIXME: div in ul-->
|
||||
<div
|
||||
v-if="!isGroupMembersDone"
|
||||
v-loading="isGroupMembersLoading"
|
||||
@@ -1150,108 +1153,75 @@
|
||||
<!--Nested-->
|
||||
<GroupPostEditDialog :dialog-data.sync="groupPostEditDialog" :selected-gallery-file="selectedGalleryFile" />
|
||||
<GroupMemberModerationDialog
|
||||
:group-dialog="groupDialog"
|
||||
:is-group-members-loading.sync="isGroupMembersLoading"
|
||||
:group-dialog-filter-options="groupDialogFilterOptions"
|
||||
:group-dialog-sorting-options="groupDialogSortingOptions"
|
||||
:random-user-colours="randomUserColours"
|
||||
:group-member-moderation="groupMemberModeration"
|
||||
@close-dialog="closeMemberModerationDialog"
|
||||
@group-members-search="groupMembersSearch"
|
||||
@load-all-group-members="loadAllGroupMembers"
|
||||
@set-group-member-filter="setGroupMemberFilter"
|
||||
@set-group-member-sort-order="setGroupMemberSortOrder" />
|
||||
<InviteGroupDialog
|
||||
:dialog-data.sync="inviteGroupDialog"
|
||||
:vip-friends="vipFriends"
|
||||
:online-friends="onlineFriends"
|
||||
:offline-friends="offlineFriends"
|
||||
:active-friends="activeFriends" />
|
||||
<InviteGroupDialog />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import * as workerTimers from 'worker-timers';
|
||||
import { groupRequest } from '../../../api';
|
||||
import utils from '../../../classes/utils';
|
||||
import { hasGroupPermission } from '../../../composables/group/utils';
|
||||
import { refreshInstancePlayerCount } from '../../../composables/instance/utils';
|
||||
import { copyToClipboard, downloadAndSaveJson, getFaviconUrl } from '../../../composables/shared/utils';
|
||||
import { languageClass } from '../../../composables/user/utils';
|
||||
import Location from '../../Location.vue';
|
||||
import { $app } from '../../../app';
|
||||
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
||||
import {
|
||||
adjustDialogZ,
|
||||
buildTreeData,
|
||||
copyToClipboard,
|
||||
downloadAndSaveJson,
|
||||
getFaviconUrl,
|
||||
hasGroupPermission,
|
||||
languageClass,
|
||||
openExternalLink,
|
||||
refreshInstancePlayerCount,
|
||||
removeFromArray,
|
||||
userImage,
|
||||
userStatusClass,
|
||||
formatDateFilter
|
||||
} from '../../../shared/utils';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useGalleryStore,
|
||||
useGroupStore,
|
||||
useLocationStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import InviteGroupDialog from '../InviteGroupDialog.vue';
|
||||
import GroupMemberModerationDialog from './GroupMemberModerationDialog.vue';
|
||||
import GroupPostEditDialog from './GroupPostEditDialog.vue';
|
||||
|
||||
const API = inject('API');
|
||||
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
|
||||
const showUserDialog = inject('showUserDialog');
|
||||
const userStatusClass = inject('userStatusClass');
|
||||
const userImage = inject('userImage');
|
||||
const openExternalLink = inject('openExternalLink');
|
||||
const adjustDialogZ = inject('adjustDialogZ');
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { showUserDialog } = useUserStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { groupDialog, inviteGroupDialog } = storeToRefs(useGroupStore());
|
||||
const {
|
||||
getGroupDialogGroup,
|
||||
updateGroupPostSearch,
|
||||
showGroupDialog,
|
||||
leaveGroupPrompt,
|
||||
setGroupVisibility,
|
||||
applyGroupMember,
|
||||
handleGroupMember,
|
||||
handleGroupMemberProps
|
||||
} = useGroupStore();
|
||||
|
||||
const { lastLocation } = storeToRefs(useLocationStore());
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $confirm = instance.proxy.$confirm;
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const props = defineProps({
|
||||
groupDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
hideTooltips: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lastLocation: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
updateInstanceInfo: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
groupDialogSortingOptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
groupDialogFilterOptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
randomUserColours: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
vipFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
onlineFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
offlineFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
activeFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:group-dialog',
|
||||
'groupDialogCommand',
|
||||
'getGroupDialogGroup',
|
||||
'updateGroupPostSearch'
|
||||
]);
|
||||
|
||||
const groupDialogRef = ref(null);
|
||||
const isGroupMembersDone = ref(false);
|
||||
const isGroupMembersLoading = ref(false);
|
||||
@@ -1283,20 +1253,10 @@
|
||||
auditLogTypes: []
|
||||
});
|
||||
|
||||
const inviteGroupDialog = ref({
|
||||
visible: false,
|
||||
loading: false,
|
||||
groupId: '',
|
||||
groupName: '',
|
||||
userId: '',
|
||||
userIds: [],
|
||||
userObject: {}
|
||||
});
|
||||
|
||||
let loadMoreGroupMembersParams = {};
|
||||
|
||||
watch(
|
||||
() => props.groupDialog.loading,
|
||||
() => groupDialog.value.loading,
|
||||
(val) => {
|
||||
if (val) {
|
||||
nextTick(() => adjustDialogZ(groupDialogRef.value.$el));
|
||||
@@ -1305,7 +1265,7 @@
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.groupDialog.isGetGroupDialogGroupLoading,
|
||||
() => groupDialog.value.isGetGroupDialogGroupLoading,
|
||||
(val) => {
|
||||
if (val) {
|
||||
getCurrentTabData();
|
||||
@@ -1314,14 +1274,9 @@
|
||||
);
|
||||
|
||||
function showInviteGroupDialog(groupId, userId) {
|
||||
const D = inviteGroupDialog.value;
|
||||
D.userIds = '';
|
||||
D.groups = [];
|
||||
D.groupId = groupId;
|
||||
D.groupName = groupId;
|
||||
D.userId = userId;
|
||||
D.userObject = {};
|
||||
D.visible = true;
|
||||
inviteGroupDialog.value.groupId = groupId;
|
||||
inviteGroupDialog.value.userId = userId;
|
||||
inviteGroupDialog.value.visible = true;
|
||||
}
|
||||
|
||||
function setGroupRepresentation(groupId) {
|
||||
@@ -1360,7 +1315,7 @@
|
||||
}
|
||||
|
||||
function groupMembersSearchDebounce() {
|
||||
const D = props.groupDialog;
|
||||
const D = groupDialog.value;
|
||||
const search = D.memberSearch;
|
||||
D.memberSearchResults = [];
|
||||
if (!search || search.length < 3) {
|
||||
@@ -1375,16 +1330,14 @@
|
||||
offset: 0
|
||||
})
|
||||
.then((args) => {
|
||||
// API.$on('GROUP:MEMBERS:SEARCH', function (args) {
|
||||
for (const json of args.json.results) {
|
||||
API.$emit('GROUP:MEMBER', {
|
||||
handleGroupMember({
|
||||
json,
|
||||
params: {
|
||||
groupId: args.params.groupId
|
||||
}
|
||||
});
|
||||
}
|
||||
// });
|
||||
if (D.id === args.params.groupId) {
|
||||
D.memberSearchResults = args.json.results;
|
||||
}
|
||||
@@ -1400,11 +1353,10 @@
|
||||
isRepresenting: isSet
|
||||
})
|
||||
.then((args) => {
|
||||
// API.$on('GROUP:SETREPRESENTATION', function (args) {
|
||||
if (props.groupDialog.visible && props.groupDialog.id === groupId) {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.groupId) {
|
||||
updateGroupDialogData({
|
||||
...props.groupDialog,
|
||||
ref: { ...props.groupDialog.ref, isRepresenting: args.params.isRepresenting }
|
||||
...groupDialog.value,
|
||||
ref: { ...groupDialog.value.ref, isRepresenting: args.params.isRepresenting }
|
||||
});
|
||||
getGroupDialogGroup(groupId);
|
||||
}
|
||||
@@ -1417,11 +1369,9 @@
|
||||
groupId: id
|
||||
})
|
||||
.then((args) => {
|
||||
// API.$on('GROUP:CANCELJOINREQUEST', function (args) {
|
||||
if (props.groupDialog.visible && props.groupDialog.id === id) {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === id) {
|
||||
getGroupDialogGroup(id);
|
||||
}
|
||||
// });
|
||||
});
|
||||
}
|
||||
function confirmDeleteGroupPost(post) {
|
||||
@@ -1437,8 +1387,7 @@
|
||||
postId: post.id
|
||||
})
|
||||
.then((args) => {
|
||||
// API.$on('GROUP:POST:DELETE', function (args) {
|
||||
const D = props.groupDialog;
|
||||
const D = groupDialog.value;
|
||||
if (D.id !== args.params.groupId) {
|
||||
return;
|
||||
}
|
||||
@@ -1447,7 +1396,7 @@
|
||||
// remove existing post
|
||||
for (const item of D.posts) {
|
||||
if (item.id === postId) {
|
||||
utils.removeFromArray(D.posts, item);
|
||||
removeFromArray(D.posts, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1460,7 +1409,6 @@
|
||||
}
|
||||
}
|
||||
updateGroupPostSearch();
|
||||
// });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1480,30 +1428,113 @@
|
||||
}
|
||||
|
||||
function groupDialogCommand(command) {
|
||||
const D = props.groupDialog;
|
||||
const D = groupDialog.value;
|
||||
if (D.visible === false) {
|
||||
return;
|
||||
}
|
||||
switch (command) {
|
||||
case 'Share':
|
||||
copyToClipboard(props.groupDialog.ref.$url);
|
||||
copyToClipboard(groupDialog.value.ref.$url);
|
||||
break;
|
||||
case 'Create Post':
|
||||
showGroupPostEditDialog(props.groupDialog.id, null);
|
||||
showGroupPostEditDialog(groupDialog.value.id, null);
|
||||
break;
|
||||
case 'Moderation Tools':
|
||||
showGroupMemberModerationDialog(props.groupDialog.id);
|
||||
showGroupMemberModerationDialog(groupDialog.value.id);
|
||||
break;
|
||||
case 'Invite To Group':
|
||||
showInviteGroupDialog(D.id, '');
|
||||
break;
|
||||
default:
|
||||
emit('groupDialogCommand', command);
|
||||
case 'Refresh':
|
||||
showGroupDialog(D.id);
|
||||
break;
|
||||
case 'Leave Group':
|
||||
leaveGroupPrompt(D.id);
|
||||
break;
|
||||
case 'Block Group':
|
||||
blockGroup(D.id);
|
||||
break;
|
||||
case 'Unblock Group':
|
||||
unblockGroup(D.id);
|
||||
break;
|
||||
case 'Visibility Everyone':
|
||||
setGroupVisibility(D.id, 'visible');
|
||||
break;
|
||||
case 'Visibility Friends':
|
||||
setGroupVisibility(D.id, 'friends');
|
||||
break;
|
||||
case 'Visibility Hidden':
|
||||
setGroupVisibility(D.id, 'hidden');
|
||||
break;
|
||||
case 'Subscribe To Announcements':
|
||||
setGroupSubscription(D.id, true);
|
||||
break;
|
||||
case 'Unsubscribe To Announcements':
|
||||
setGroupSubscription(D.id, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function setGroupSubscription(groupId, subscribe) {
|
||||
return groupRequest
|
||||
.setGroupMemberProps(currentUser.value.id, groupId, {
|
||||
isSubscribedToAnnouncements: subscribe
|
||||
})
|
||||
.then((args) => {
|
||||
handleGroupMemberProps(args);
|
||||
$app.$message({
|
||||
message: 'Group subscription updated',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
function blockGroup(groupId) {
|
||||
$app.$confirm('Are you sure you want to block this group?', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
groupRequest
|
||||
.blockGroup({
|
||||
groupId
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function unblockGroup(groupId) {
|
||||
$app.$confirm('Are you sure you want to unblock this group?', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
groupRequest
|
||||
.unblockGroup({
|
||||
groupId,
|
||||
userId: currentUser.value.id
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showGroupMemberModerationDialog(groupId) {
|
||||
if (groupId !== props.groupDialog.id) {
|
||||
if (groupId !== groupDialog.value.id) {
|
||||
return;
|
||||
}
|
||||
const D = groupMemberModeration;
|
||||
@@ -1511,16 +1542,14 @@
|
||||
|
||||
D.groupRef = {};
|
||||
D.auditLogTypes = [];
|
||||
API.getCachedGroup({ groupId }).then((args) => {
|
||||
groupRequest.getCachedGroup({ groupId }).then((args) => {
|
||||
D.groupRef = args.ref;
|
||||
if (hasGroupPermission(D.groupRef, 'group-audit-view')) {
|
||||
groupRequest.getGroupAuditLogTypes({ groupId }).then((args) => {
|
||||
// API.$on('GROUP:AUDITLOGTYPES', function (args) {
|
||||
if (groupMemberModeration.id !== args.params.groupId) {
|
||||
return;
|
||||
}
|
||||
groupMemberModeration.auditLogTypes = args.json;
|
||||
// });
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1535,16 +1564,14 @@
|
||||
groupId: id
|
||||
})
|
||||
.then((args) => {
|
||||
// API.$on('GROUP:JOIN', function (args) {
|
||||
if (props.groupDialog.visible && props.groupDialog.id === id) {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === id) {
|
||||
updateGroupDialogData({
|
||||
...props.groupDialog,
|
||||
...groupDialog.value,
|
||||
inGroup: args.json.membershipStatus === 'member'
|
||||
});
|
||||
// props.groupDialog.inGroup = args.json.membershipStatus === 'member';
|
||||
// groupDialog.value.inGroup = json.membershipStatus === 'member';
|
||||
getGroupDialogGroup(id);
|
||||
}
|
||||
// });
|
||||
if (args.json.membershipStatus === 'member') {
|
||||
$message({
|
||||
message: 'Group joined',
|
||||
@@ -1595,14 +1622,14 @@
|
||||
selectedImageUrl: post.imageUrl
|
||||
};
|
||||
}
|
||||
API.getCachedGroup({ groupId }).then((args) => {
|
||||
groupRequest.getCachedGroup({ groupId }).then((args) => {
|
||||
D.groupRef = args.ref;
|
||||
});
|
||||
D.visible = true;
|
||||
}
|
||||
|
||||
async function getGroupDialogGroupMembers() {
|
||||
const D = props.groupDialog;
|
||||
const D = groupDialog.value;
|
||||
D.members = [];
|
||||
isGroupMembersDone.value = false;
|
||||
loadMoreGroupMembersParams = {
|
||||
@@ -1620,12 +1647,12 @@
|
||||
await groupRequest
|
||||
.getGroupMember({
|
||||
groupId: D.id,
|
||||
userId: API.currentUser.id
|
||||
userId: currentUser.value.id
|
||||
})
|
||||
.then((args) => {
|
||||
args.ref = API.applyGroupMember(args.json);
|
||||
args.ref = applyGroupMember(args.json);
|
||||
if (args.json) {
|
||||
args.json.user = API.currentUser;
|
||||
args.json.user = currentUser.value;
|
||||
if (D.memberFilter.id === null) {
|
||||
// when flitered by role don't include self
|
||||
D.members.push(args.json);
|
||||
@@ -1641,7 +1668,7 @@
|
||||
if (isGroupMembersDone.value || isGroupMembersLoading.value) {
|
||||
return;
|
||||
}
|
||||
const D = props.groupDialog;
|
||||
const D = groupDialog.value;
|
||||
const params = loadMoreGroupMembersParams;
|
||||
D.memberSearch = '';
|
||||
isGroupMembersLoading.value = true;
|
||||
@@ -1651,10 +1678,18 @@
|
||||
isGroupMembersLoading.value = false;
|
||||
})
|
||||
.then((args) => {
|
||||
for (const json of args.json) {
|
||||
handleGroupMember({
|
||||
json,
|
||||
params: {
|
||||
groupId: args.params.groupId
|
||||
}
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < args.json.length; i++) {
|
||||
const member = args.json[i];
|
||||
if (member.userId === API.currentUser.id) {
|
||||
if (D.members.length > 0 && D.members[0].userId === API.currentUser.id) {
|
||||
if (member.userId === currentUser.value.id) {
|
||||
if (D.members.length > 0 && D.members[0].userId === currentUser.value.id) {
|
||||
// remove duplicate and keep sort order
|
||||
D.members.splice(0, 1);
|
||||
}
|
||||
@@ -1685,12 +1720,12 @@
|
||||
}
|
||||
|
||||
async function getGroupGalleries() {
|
||||
updateGroupDialogData({ ...props.groupDialog, galleries: {} });
|
||||
updateGroupDialogData({ ...groupDialog.value, galleries: {} });
|
||||
groupDialogGalleryCurrentName.value = '0';
|
||||
isGroupGalleryLoading.value = true;
|
||||
for (let i = 0; i < props.groupDialog.ref.galleries.length; i++) {
|
||||
const gallery = props.groupDialog.ref.galleries[i];
|
||||
await getGroupGallery(props.groupDialog.id, gallery.id);
|
||||
for (let i = 0; i < groupDialog.value.ref.galleries.length; i++) {
|
||||
const gallery = groupDialog.value.ref.galleries[i];
|
||||
await getGroupGallery(groupDialog.value.id, gallery.id);
|
||||
}
|
||||
isGroupGalleryLoading.value = false;
|
||||
}
|
||||
@@ -1707,16 +1742,14 @@
|
||||
for (let i = 0; i < count; i++) {
|
||||
const args = await groupRequest.getGroupGallery(params);
|
||||
if (args) {
|
||||
// API.$on('GROUP:GALLERY', function (args) {
|
||||
for (const json of args.json) {
|
||||
if (props.groupDialog.id === json.groupId) {
|
||||
if (!props.groupDialog.galleries[json.galleryId]) {
|
||||
props.groupDialog.galleries[json.galleryId] = [];
|
||||
if (groupDialog.value.id === json.groupId) {
|
||||
if (!groupDialog.value.galleries[json.galleryId]) {
|
||||
groupDialog.value.galleries[json.galleryId] = [];
|
||||
}
|
||||
props.groupDialog.galleries[json.galleryId].push(json);
|
||||
groupDialog.value.galleries[json.galleryId].push(json);
|
||||
}
|
||||
}
|
||||
// });
|
||||
}
|
||||
params.offset += 100;
|
||||
if (args.json.length < 100) {
|
||||
@@ -1729,8 +1762,8 @@
|
||||
}
|
||||
|
||||
function refreshGroupDialogTreeData() {
|
||||
const D = props.groupDialog;
|
||||
const treeData = utils.buildTreeData({
|
||||
const D = groupDialog.value;
|
||||
const treeData = buildTreeData({
|
||||
group: D.ref,
|
||||
posts: D.posts,
|
||||
instances: D.instances,
|
||||
@@ -1738,7 +1771,7 @@
|
||||
galleries: D.galleries
|
||||
});
|
||||
updateGroupDialogData({
|
||||
...props.groupDialog,
|
||||
...groupDialog.value,
|
||||
treeData
|
||||
});
|
||||
}
|
||||
@@ -1748,19 +1781,19 @@
|
||||
return;
|
||||
}
|
||||
await getGroupDialogGroupMembers();
|
||||
while (props.groupDialog.visible && !isGroupMembersDone.value) {
|
||||
while (groupDialog.value.visible && !isGroupMembersDone.value) {
|
||||
isGroupMembersLoading.value = true;
|
||||
await new Promise((resolve) => {
|
||||
workerTimers.setTimeout(resolve, 1000);
|
||||
});
|
||||
isGroupMembersLoading.value = false;
|
||||
await this.loadMoreGroupMembers();
|
||||
await loadMoreGroupMembers();
|
||||
}
|
||||
}
|
||||
|
||||
async function setGroupMemberSortOrder(sortOrder) {
|
||||
const D = props.groupDialog;
|
||||
if (D.memberSortOrder === sortOrder) {
|
||||
const D = groupDialog.value;
|
||||
if (D.memberSortOrder.value === sortOrder) {
|
||||
return;
|
||||
}
|
||||
D.memberSortOrder = sortOrder;
|
||||
@@ -1768,8 +1801,8 @@
|
||||
}
|
||||
|
||||
async function setGroupMemberFilter(filter) {
|
||||
const D = props.groupDialog;
|
||||
if (D.memberFilter === filter) {
|
||||
const D = groupDialog.value;
|
||||
if (D.memberFilter.value === filter) {
|
||||
return;
|
||||
}
|
||||
D.memberFilter = filter;
|
||||
@@ -1777,13 +1810,9 @@
|
||||
}
|
||||
|
||||
function updateGroupDialogData(obj) {
|
||||
// Be careful with the deep merge
|
||||
emit('update:group-dialog', obj);
|
||||
}
|
||||
function getGroupDialogGroup(groupId) {
|
||||
emit('getGroupDialogGroup', groupId);
|
||||
}
|
||||
function updateGroupPostSearch() {
|
||||
emit('updateGroupPostSearch');
|
||||
groupDialog.value = {
|
||||
...groupDialog.value,
|
||||
...obj
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
icon="el-icon-refresh"
|
||||
:loading="isGroupMembersLoading"
|
||||
circle
|
||||
@click="loadAllGroupMembers"></el-button>
|
||||
@click="loadAllGroupMembers" />
|
||||
<span style="font-size: 14px; margin-left: 5px; margin-right: 5px">
|
||||
{{ groupMemberModerationTable.data.length }}/{{
|
||||
groupMemberModeration.groupRef.memberCount
|
||||
@@ -40,7 +40,7 @@
|
||||
@click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span
|
||||
>{{ groupDialog.memberSortOrder.name }}
|
||||
>{{ t(groupDialog.memberSortOrder.name) }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i
|
||||
></span>
|
||||
</el-button>
|
||||
@@ -49,7 +49,7 @@
|
||||
v-for="item in groupDialogSortingOptions"
|
||||
:key="item.name"
|
||||
@click.native="setGroupMemberSortOrder(item)">
|
||||
{{ item.name }}
|
||||
{{ t(item.name) }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
@@ -68,7 +68,7 @@
|
||||
@click.native.stop>
|
||||
<el-button size="mini">
|
||||
<span
|
||||
>{{ groupDialog.memberFilter.name }}
|
||||
>{{ t(groupDialog.memberFilter.name) }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i
|
||||
></span>
|
||||
</el-button>
|
||||
@@ -77,7 +77,7 @@
|
||||
v-for="item in groupDialogFilterOptions"
|
||||
:key="item.name"
|
||||
@click.native="setGroupMemberFilter(item)"
|
||||
v-text="item.name"></el-dropdown-item>
|
||||
v-text="t(item.name)"></el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-for="item in groupDialog.ref.roles"
|
||||
v-if="!item.defaultRole"
|
||||
@@ -147,12 +147,11 @@
|
||||
<el-table-column :label="t('dialog.group_member_moderation.roles')" prop="roleIds" sortable>
|
||||
<template slot-scope="scope">
|
||||
<template v-for="(roleId, index) in scope.row.roleIds">
|
||||
<span
|
||||
v-for="(role, rIndex) in groupMemberModeration.groupRef.roles"
|
||||
v-if="role?.id === roleId"
|
||||
:key="rIndex"
|
||||
>{{ role.name
|
||||
}}<span v-if="index < scope.row.roleIds.length - 1">, </span></span
|
||||
<template v-for="(role, rIndex) in groupMemberModeration.groupRef.roles">
|
||||
<span v-if="role?.id === roleId" :key="roleId + rIndex"
|
||||
>{{ role.name
|
||||
}}<span v-if="index < scope.row.roleIds.length - 1">, </span></span
|
||||
></template
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
@@ -171,7 +170,7 @@
|
||||
prop="joinedAt"
|
||||
sortable>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.joinedAt | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.joinedAt, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -284,7 +283,7 @@
|
||||
prop="joinedAt"
|
||||
sortable>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.joinedAt | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.joinedAt, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -293,7 +292,7 @@
|
||||
prop="bannedAt"
|
||||
sortable>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.bannedAt | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.bannedAt, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</data-tables>
|
||||
@@ -629,7 +628,7 @@
|
||||
prop="created_at"
|
||||
sortable>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.created_at | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -656,7 +655,7 @@
|
||||
:label="t('dialog.group_member_moderation.description')"
|
||||
prop="description">
|
||||
<template slot-scope="scope">
|
||||
<location
|
||||
<Location
|
||||
v-if="scope.row?.targetId.startsWith('wrld_')"
|
||||
:location="scope.row.targetId" />
|
||||
<span v-text="scope.row.description"></span>
|
||||
@@ -810,45 +809,165 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Location from '../../Location.vue';
|
||||
import { getCurrentInstance, inject, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, reactive, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { groupRequest, userRequest } from '../../../api';
|
||||
import { useModerationTable, useSelectedUsers } from '../../../composables/group/useGroupMemberModeration';
|
||||
import { hasGroupPermission } from '../../../composables/group/utils';
|
||||
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
||||
import { hasGroupPermission, userImage, userImageFull, formatDateFilter } from '../../../shared/utils';
|
||||
import { useAppearanceSettingsStore, useGalleryStore, useGroupStore, useUserStore } from '../../../stores';
|
||||
import GroupMemberModerationExportDialog from './GroupMemberModerationExportDialog.vue';
|
||||
|
||||
const API = inject('API');
|
||||
const showUserDialog = inject('showUserDialog');
|
||||
const userImage = inject('userImage');
|
||||
const userImageFull = inject('userImageFull');
|
||||
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
|
||||
|
||||
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { showUserDialog } = useUserStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { groupDialog } = storeToRefs(useGroupStore());
|
||||
const { applyGroupMember, handleGroupMemberProps } = useGroupStore();
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const selectedUsers = reactive({});
|
||||
const selectedUsersArray = ref([]);
|
||||
|
||||
function setSelectedUsers(usersId, user) {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
selectedUsers[usersId] = user;
|
||||
selectedUsersArray.value = Object.values(selectedUsers);
|
||||
}
|
||||
|
||||
function deselectedUsers(userId, isAll = false) {
|
||||
if (isAll) {
|
||||
for (const id in selectedUsers) {
|
||||
if (Object.prototype.hasOwnProperty.call(selectedUsers, id)) {
|
||||
delete selectedUsers[id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Object.prototype.hasOwnProperty.call(selectedUsers, userId)) {
|
||||
delete selectedUsers[userId];
|
||||
}
|
||||
}
|
||||
selectedUsersArray.value = Object.values(selectedUsers);
|
||||
}
|
||||
|
||||
function groupMemberModerationTableSelectionChange(row) {
|
||||
if (row.$selected && !selectedUsers[row.userId]) {
|
||||
setSelectedUsers(row.userId, row);
|
||||
} else if (!row.$selected && selectedUsers[row.userId]) {
|
||||
deselectedUsers(row.userId);
|
||||
}
|
||||
}
|
||||
|
||||
const groupInvitesModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupJoinRequestsModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupBlockedModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupLogsModerationTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: ['description'], value: '' }],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupBansModerationTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: ['$displayName'], value: '' }],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupMemberModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
|
||||
async function initializePageSize() {
|
||||
try {
|
||||
const { tablePageSize } = storeToRefs(useAppearanceSettingsStore());
|
||||
|
||||
groupMemberModerationTable.pageSize = tablePageSize.value;
|
||||
groupBansModerationTable.pageSize = tablePageSize.value;
|
||||
groupLogsModerationTable.pageSize = tablePageSize.value;
|
||||
groupInvitesModerationTable.pageSize = tablePageSize.value;
|
||||
groupJoinRequestsModerationTable.pageSize = tablePageSize.value;
|
||||
groupBlockedModerationTable.pageSize = tablePageSize.value;
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize table page size:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function deselectGroupMember(userId) {
|
||||
const deselectInTable = (tableData) => {
|
||||
if (userId) {
|
||||
const row = tableData.find((item) => item.userId === userId);
|
||||
if (row) {
|
||||
row.$selected = false;
|
||||
}
|
||||
} else {
|
||||
tableData.forEach((row) => {
|
||||
if (row.$selected) {
|
||||
row.$selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deselectInTable(groupMemberModerationTable.data);
|
||||
deselectInTable(groupBansModerationTable.data);
|
||||
deselectInTable(groupInvitesModerationTable.data);
|
||||
deselectInTable(groupJoinRequestsModerationTable.data);
|
||||
deselectInTable(groupBlockedModerationTable.data);
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
isGroupMembersLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
groupDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
groupDialogSortingOptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
groupDialogFilterOptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
randomUserColours: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
groupMemberModeration: {
|
||||
type: Object,
|
||||
required: true
|
||||
@@ -864,25 +983,6 @@
|
||||
'group-members-search'
|
||||
]);
|
||||
|
||||
const {
|
||||
groupInvitesModerationTable,
|
||||
groupJoinRequestsModerationTable,
|
||||
groupBlockedModerationTable,
|
||||
groupLogsModerationTable,
|
||||
groupBansModerationTable,
|
||||
groupMemberModerationTable,
|
||||
initializePageSize,
|
||||
deselectGroupMember
|
||||
} = useModerationTable();
|
||||
|
||||
const {
|
||||
selectedUsers,
|
||||
selectedUsersArray,
|
||||
groupMemberModerationTableSelectionChange,
|
||||
deselectedUsers,
|
||||
setSelectedUsers
|
||||
} = useSelectedUsers();
|
||||
|
||||
const selectUserId = ref('');
|
||||
const progressCurrent = ref(0);
|
||||
const progressTotal = ref(0);
|
||||
@@ -895,7 +995,7 @@
|
||||
() => props.groupMemberModeration.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
if (props.groupMemberModeration.id !== props.groupDialog.id) {
|
||||
if (props.groupMemberModeration.id !== groupDialog.value.id) {
|
||||
return;
|
||||
}
|
||||
groupMemberModerationTable.data = [];
|
||||
@@ -905,6 +1005,7 @@
|
||||
groupBlockedModerationTable.data = [];
|
||||
groupLogsModerationTable.data = [];
|
||||
Object.assign(selectedUsers, {});
|
||||
selectedUsersArray.value = [];
|
||||
selectUserId.value = '';
|
||||
selectedRoles.value = [];
|
||||
note.value = '';
|
||||
@@ -913,7 +1014,7 @@
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.groupDialog.members,
|
||||
() => groupDialog.value.members,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
setGroupMemberModerationTable(newVal);
|
||||
@@ -923,7 +1024,7 @@
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.groupDialog.memberSearchResults,
|
||||
() => groupDialog.value.memberSearchResults,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
setGroupMemberModerationTable(newVal);
|
||||
@@ -965,9 +1066,8 @@
|
||||
}
|
||||
|
||||
function handleGroupMemberRoleChange(args) {
|
||||
// 'GROUP:MEMBER:ROLE:CHANGE'
|
||||
if (props.groupDialog.id === args.params.groupId) {
|
||||
props.groupDialog.members.forEach((member) => {
|
||||
if (groupDialog.value.id === args.params.groupId) {
|
||||
groupDialog.value.members.forEach((member) => {
|
||||
if (member.userId === args.params.userId) {
|
||||
member.roleIds = args.json;
|
||||
return true;
|
||||
@@ -988,7 +1088,7 @@
|
||||
}
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) {
|
||||
if (user.userId === currentUser.value.id) {
|
||||
continue;
|
||||
}
|
||||
console.log(`Deleting group invite ${user.userId} ${i + 1}/${memberCount}`);
|
||||
@@ -1014,7 +1114,7 @@
|
||||
}
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
getAllGroupInvites(D.id, groupInvitesModerationTable);
|
||||
getAllGroupInvites(D.id);
|
||||
}
|
||||
|
||||
function selectAllGroupMembers() {
|
||||
@@ -1038,7 +1138,7 @@
|
||||
continue;
|
||||
}
|
||||
args.json.forEach((json) => {
|
||||
const ref = API.applyGroupMember(json);
|
||||
const ref = applyGroupMember(json);
|
||||
fetchedBans.push(ref);
|
||||
});
|
||||
if (args.json.length < params.n) {
|
||||
@@ -1076,7 +1176,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
console.log(`Banning ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
await groupRequest.banGroupMember({ groupId: D.id, userId: user.userId });
|
||||
@@ -1105,7 +1205,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
console.log(`Unbanning ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
await groupRequest.unbanGroupMember({ groupId: D.id, userId: user.userId });
|
||||
@@ -1138,7 +1238,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
|
||||
console.log(`Kicking ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
@@ -1158,6 +1258,7 @@
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
deselectedUsers(null, true);
|
||||
loadAllGroupMembers();
|
||||
}
|
||||
|
||||
async function groupMembersSaveNote() {
|
||||
@@ -1176,7 +1277,8 @@
|
||||
}
|
||||
console.log(`Setting note ${noteToSave} for ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
await groupRequest.setGroupMemberProps(user.userId, D.id, { managerNotes: noteToSave });
|
||||
const args = await groupRequest.setGroupMemberProps(user.userId, D.id, { managerNotes: noteToSave });
|
||||
handleGroupMemberProps(args);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
$message({
|
||||
@@ -1334,7 +1436,7 @@
|
||||
let member = {};
|
||||
const memberArgs = await groupRequest.getGroupMember({ groupId: D.id, userId });
|
||||
if (memberArgs && memberArgs.json) {
|
||||
member = API.applyGroupMember(memberArgs.json);
|
||||
member = applyGroupMember(memberArgs.json);
|
||||
}
|
||||
if (member && member.user) {
|
||||
setSelectedUsers(member.userId, member);
|
||||
@@ -1400,7 +1502,7 @@
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
|
||||
if (user.userId === API.currentUser.id) {
|
||||
if (user.userId === currentUser.value.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1424,7 +1526,7 @@
|
||||
}
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
getAllGroupInvitesAndJoinRequests();
|
||||
getAllGroupInvitesAndJoinRequests(D.id);
|
||||
deselectedUsers(null, true);
|
||||
}
|
||||
|
||||
@@ -1438,7 +1540,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
|
||||
console.log(`Blocking group join request from ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
@@ -1460,7 +1562,7 @@
|
||||
}
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
getAllGroupInvitesAndJoinRequests();
|
||||
getAllGroupInvitesAndJoinRequests(D.id);
|
||||
deselectedUsers(null, true);
|
||||
}
|
||||
|
||||
@@ -1475,7 +1577,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
|
||||
console.log(`Rejecting group join request from ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
@@ -1497,7 +1599,7 @@
|
||||
}
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
getAllGroupInvitesAndJoinRequests();
|
||||
getAllGroupInvitesAndJoinRequests(D.id);
|
||||
deselectedUsers(null, true);
|
||||
}
|
||||
|
||||
@@ -1511,7 +1613,7 @@
|
||||
if (!progressTotal.value) break;
|
||||
const user = users[i];
|
||||
progressCurrent.value = i + 1;
|
||||
if (user.userId === API.currentUser.id) continue;
|
||||
if (user.userId === currentUser.value.id) continue;
|
||||
|
||||
console.log(`Accepting group join request from ${user.userId} ${i + 1}/${memberCount}`);
|
||||
try {
|
||||
@@ -1533,7 +1635,7 @@
|
||||
}
|
||||
progressCurrent.value = 0;
|
||||
progressTotal.value = 0;
|
||||
getAllGroupInvitesAndJoinRequests();
|
||||
getAllGroupInvitesAndJoinRequests(D.id);
|
||||
deselectedUsers(null, true);
|
||||
}
|
||||
|
||||
@@ -1565,7 +1667,7 @@
|
||||
? groupBlockedModerationTable
|
||||
: groupJoinRequestsModerationTable;
|
||||
for (const json of args.json) {
|
||||
const ref = API.applyGroupMember(json);
|
||||
const ref = applyGroupMember(json);
|
||||
targetTable.data.push(ref);
|
||||
}
|
||||
params.offset += params.n;
|
||||
@@ -1598,7 +1700,7 @@
|
||||
? groupBlockedModerationTable
|
||||
: groupJoinRequestsModerationTable;
|
||||
for (const json of args.json) {
|
||||
const ref = API.applyGroupMember(json);
|
||||
const ref = applyGroupMember(json);
|
||||
targetTable.data.push(ref);
|
||||
}
|
||||
params.offset += params.n;
|
||||
@@ -1631,7 +1733,7 @@
|
||||
}
|
||||
|
||||
for (const json of args.json) {
|
||||
const ref = API.applyGroupMember(json);
|
||||
const ref = applyGroupMember(json);
|
||||
groupInvitesModerationTable.data.push(ref);
|
||||
}
|
||||
}
|
||||
@@ -1649,7 +1751,7 @@
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
updateIsGroupMembersLoading(false); // Use emit
|
||||
updateIsGroupMembersLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { copyToClipboard } from '../../../composables/shared/utils';
|
||||
import { copyToClipboard } from '../../../shared/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
:visible.sync="groupPostEditDialog.visible"
|
||||
:title="$t('dialog.group_post_edit.header')"
|
||||
:title="t('dialog.group_post_edit.header')"
|
||||
width="650px"
|
||||
append-to-body>
|
||||
<div v-if="groupPostEditDialog.visible">
|
||||
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
|
||||
<el-form :model="groupPostEditDialog" label-width="150px">
|
||||
<el-form-item :label="$t('dialog.group_post_edit.title')">
|
||||
<el-form-item :label="t('dialog.group_post_edit.title')">
|
||||
<el-input v-model="groupPostEditDialog.title" size="mini"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dialog.group_post_edit.message')">
|
||||
<el-form-item :label="t('dialog.group_post_edit.message')">
|
||||
<el-input
|
||||
v-model="groupPostEditDialog.text"
|
||||
type="textarea"
|
||||
@@ -24,29 +24,27 @@
|
||||
v-if="!groupPostEditDialog.postId"
|
||||
v-model="groupPostEditDialog.sendNotification"
|
||||
size="small">
|
||||
{{ $t('dialog.group_post_edit.send_notification') }}
|
||||
{{ t('dialog.group_post_edit.send_notification') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dialog.group_post_edit.post_visibility')">
|
||||
<el-form-item :label="t('dialog.group_post_edit.post_visibility')">
|
||||
<el-radio-group v-model="groupPostEditDialog.visibility" size="small">
|
||||
<el-radio label="public">
|
||||
{{ $t('dialog.group_post_edit.visibility_public') }}
|
||||
{{ t('dialog.group_post_edit.visibility_public') }}
|
||||
</el-radio>
|
||||
<el-radio label="group">
|
||||
{{ $t('dialog.group_post_edit.visibility_group') }}
|
||||
{{ t('dialog.group_post_edit.visibility_group') }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="groupPostEditDialog.visibility === 'group'"
|
||||
:label="$t('dialog.new_instance.roles')">
|
||||
<el-form-item v-if="groupPostEditDialog.visibility === 'group'" :label="t('dialog.new_instance.roles')">
|
||||
<el-select
|
||||
v-model="groupPostEditDialog.roleIds"
|
||||
multiple
|
||||
clearable
|
||||
:placeholder="$t('dialog.new_instance.role_placeholder')"
|
||||
:placeholder="t('dialog.new_instance.role_placeholder')"
|
||||
style="width: 100%">
|
||||
<el-option-group :label="$t('dialog.new_instance.role_placeholder')">
|
||||
<el-option-group :label="t('dialog.new_instance.role_placeholder')">
|
||||
<el-option
|
||||
v-for="role in groupPostEditDialog.groupRef?.roles"
|
||||
:key="role.id"
|
||||
@@ -60,7 +58,7 @@
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dialog.group_post_edit.image')">
|
||||
<el-form-item :label="t('dialog.group_post_edit.image')">
|
||||
<template v-if="gallerySelectDialog.selectedFileId">
|
||||
<div style="display: inline-block; flex: none; margin-right: 5px">
|
||||
<el-popover placement="right" width="500px" trigger="click">
|
||||
@@ -80,13 +78,13 @@
|
||||
@click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)" />
|
||||
</el-popover>
|
||||
<el-button size="mini" style="vertical-align: top" @click="clearImageGallerySelect">
|
||||
{{ $t('dialog.invite_message.clear_selected_image') }}
|
||||
{{ t('dialog.invite_message.clear_selected_image') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button size="mini" style="margin-right: 5px" @click="showGallerySelectDialog">
|
||||
{{ $t('dialog.invite_message.select_image') }}
|
||||
{{ t('dialog.invite_message.select_image') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-form-item>
|
||||
@@ -94,13 +92,13 @@
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button size="small" @click="groupPostEditDialog.visible = false">
|
||||
{{ $t('dialog.group_post_edit.cancel') }}
|
||||
{{ t('dialog.group_post_edit.cancel') }}
|
||||
</el-button>
|
||||
<el-button v-if="groupPostEditDialog.postId" size="small" @click="editGroupPost">
|
||||
{{ $t('dialog.group_post_edit.edit_post') }}
|
||||
{{ t('dialog.group_post_edit.edit_post') }}
|
||||
</el-button>
|
||||
<el-button v-else size="small" @click="createGroupPost">
|
||||
{{ $t('dialog.group_post_edit.create_post') }}
|
||||
{{ t('dialog.group_post_edit.create_post') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<GallerySelectDialog
|
||||
@@ -110,114 +108,115 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { groupRequest, vrcPlusIconRequest } from '../../../api';
|
||||
import { useGalleryStore, useGroupStore } from '../../../stores';
|
||||
import GallerySelectDialog from './GallerySelectDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'GroupPostEditDialog',
|
||||
components: {
|
||||
GallerySelectDialog
|
||||
const props = defineProps({
|
||||
dialogData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
inject: ['showFullscreenImageDialog'],
|
||||
props: {
|
||||
dialogData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
selectedGalleryFile: { type: Object, default: () => ({}) }
|
||||
selectedGalleryFile: { type: Object, default: () => ({}) }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:dialogData']);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { t } = useI18n();
|
||||
|
||||
const { showFullscreenImageDialog, handleFilesList } = useGalleryStore();
|
||||
const { handleGroupPost } = useGroupStore();
|
||||
|
||||
const gallerySelectDialog = ref({
|
||||
visible: false,
|
||||
selectedFileId: '',
|
||||
selectedImageUrl: ''
|
||||
});
|
||||
const galleryTable = ref([]);
|
||||
|
||||
const groupPostEditDialog = computed({
|
||||
get() {
|
||||
return props.dialogData;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
gallerySelectDialog: {
|
||||
visible: false,
|
||||
selectedFileId: '',
|
||||
selectedImageUrl: ''
|
||||
},
|
||||
galleryTable: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
groupPostEditDialog: {
|
||||
get() {
|
||||
return this.dialogData;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:dialog-data', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showGallerySelectDialog() {
|
||||
const D = this.gallerySelectDialog;
|
||||
D.visible = true;
|
||||
this.refreshGalleryTable();
|
||||
},
|
||||
async refreshGalleryTable() {
|
||||
const params = {
|
||||
n: 100,
|
||||
tag: 'gallery'
|
||||
};
|
||||
const args = await vrcPlusIconRequest.getFileList(params);
|
||||
// API.$on('FILES:LIST')
|
||||
if (args.params.tag === 'gallery') {
|
||||
this.galleryTable = args.json.reverse();
|
||||
}
|
||||
},
|
||||
editGroupPost() {
|
||||
const D = this.groupPostEditDialog;
|
||||
if (!D.groupId || !D.postId) {
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
groupId: D.groupId,
|
||||
postId: D.postId,
|
||||
title: D.title,
|
||||
text: D.text,
|
||||
roleIds: D.roleIds,
|
||||
visibility: D.visibility,
|
||||
imageId: null
|
||||
};
|
||||
if (this.gallerySelectDialog.selectedFileId) {
|
||||
params.imageId = this.gallerySelectDialog.selectedFileId;
|
||||
}
|
||||
groupRequest.editGroupPost(params).then((args) => {
|
||||
this.$message({
|
||||
message: 'Group post edited',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
},
|
||||
createGroupPost() {
|
||||
const D = this.groupPostEditDialog;
|
||||
const params = {
|
||||
groupId: D.groupId,
|
||||
title: D.title,
|
||||
text: D.text,
|
||||
roleIds: D.roleIds,
|
||||
visibility: D.visibility,
|
||||
sendNotification: D.sendNotification,
|
||||
imageId: null
|
||||
};
|
||||
if (this.gallerySelectDialog.selectedFileId) {
|
||||
params.imageId = this.gallerySelectDialog.selectedFileId;
|
||||
}
|
||||
groupRequest.createGroupPost(params).then((args) => {
|
||||
this.$message({
|
||||
message: 'Group post created',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
},
|
||||
clearImageGallerySelect() {
|
||||
const D = this.gallerySelectDialog;
|
||||
D.selectedFileId = '';
|
||||
D.selectedImageUrl = '';
|
||||
}
|
||||
set(value) {
|
||||
emit('update:dialogData', value);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function showGallerySelectDialog() {
|
||||
const D = gallerySelectDialog.value;
|
||||
D.visible = true;
|
||||
refreshGalleryTable();
|
||||
}
|
||||
async function refreshGalleryTable() {
|
||||
const params = {
|
||||
n: 100,
|
||||
tag: 'gallery'
|
||||
};
|
||||
const args = await vrcPlusIconRequest.getFileList(params);
|
||||
handleFilesList(args);
|
||||
if (args.params.tag === 'gallery') {
|
||||
galleryTable.value = args.json.reverse();
|
||||
}
|
||||
}
|
||||
function editGroupPost() {
|
||||
const D = groupPostEditDialog.value;
|
||||
if (!D.groupId || !D.postId) {
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
groupId: D.groupId,
|
||||
postId: D.postId,
|
||||
title: D.title,
|
||||
text: D.text,
|
||||
roleIds: D.roleIds,
|
||||
visibility: D.visibility,
|
||||
imageId: null
|
||||
};
|
||||
if (gallerySelectDialog.value.selectedFileId) {
|
||||
params.imageId = gallerySelectDialog.value.selectedFileId;
|
||||
}
|
||||
groupRequest.editGroupPost(params).then((args) => {
|
||||
handleGroupPost();
|
||||
proxy.$message({
|
||||
message: 'Group post edited',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
}
|
||||
function createGroupPost() {
|
||||
const D = groupPostEditDialog.value;
|
||||
const params = {
|
||||
groupId: D.groupId,
|
||||
title: D.title,
|
||||
text: D.text,
|
||||
roleIds: D.roleIds,
|
||||
visibility: D.visibility,
|
||||
sendNotification: D.sendNotification,
|
||||
imageId: null
|
||||
};
|
||||
if (gallerySelectDialog.value.selectedFileId) {
|
||||
params.imageId = gallerySelectDialog.value.selectedFileId;
|
||||
}
|
||||
groupRequest.createGroupPost(params).then((args) => {
|
||||
handleGroupPost();
|
||||
proxy.$message({
|
||||
message: 'Group post created',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
}
|
||||
function clearImageGallerySelect() {
|
||||
const D = gallerySelectDialog.value;
|
||||
D.selectedFileId = '';
|
||||
D.selectedImageUrl = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -32,17 +32,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { instanceRequest, inviteMessagesRequest, notificationRequest } from '../../../api';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import { parseLocation } from '../../../shared/utils';
|
||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const API = inject('API');
|
||||
const clearInviteImageUpload = inject('clearInviteImageUpload');
|
||||
const { uploadImage } = storeToRefs(useGalleryStore());
|
||||
const { clearInviteImageUpload } = useGalleryStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
editAndSendInviteDialog: {
|
||||
@@ -57,9 +60,6 @@
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
throw err;
|
||||
})
|
||||
.then((args) => {
|
||||
API.$emit(`INVITE:${messageType.toUpperCase()}`, args);
|
||||
if (args.json[slot].message === I.messageSlot.message) {
|
||||
$message({
|
||||
message: "VRChat API didn't update message, try again",
|
||||
@@ -103,7 +102,7 @@
|
||||
const inviteLoop = () => {
|
||||
if (J.userIds.length > 0) {
|
||||
const receiverUserId = J.userIds.shift();
|
||||
if (receiverUserId === API.currentUser.id) {
|
||||
if (receiverUserId === currentUser.value.id) {
|
||||
// can't invite self!?
|
||||
const L = parseLocation(J.worldId);
|
||||
instanceRequest
|
||||
@@ -112,7 +111,7 @@
|
||||
worldId: L.worldId
|
||||
})
|
||||
.finally(inviteLoop);
|
||||
} else if (props.uploadImage) {
|
||||
} else if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendInvitePhoto(
|
||||
{
|
||||
@@ -149,7 +148,7 @@
|
||||
inviteLoop();
|
||||
} else if (messageType === 'invite') {
|
||||
I.params.messageSlot = slot;
|
||||
if (props.uploadImage) {
|
||||
if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendInvitePhoto(I.params, I.userId)
|
||||
.catch((err) => {
|
||||
@@ -178,7 +177,7 @@
|
||||
}
|
||||
} else if (messageType === 'request') {
|
||||
I.params.requestSlot = slot;
|
||||
if (props.uploadImage) {
|
||||
if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendRequestInvitePhoto(I.params, I.userId)
|
||||
.catch((err) => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
width="500px"
|
||||
append-to-body>
|
||||
<div v-if="inviteDialog.visible" v-loading="inviteDialog.loading">
|
||||
<location :location="inviteDialog.worldId" :link="false"></location>
|
||||
<Location :location="inviteDialog.worldId" :link="false" />
|
||||
<br />
|
||||
<el-button size="mini" style="margin-top: 10px" @click="addSelfToInvite">{{
|
||||
t('dialog.invite.add_self')
|
||||
@@ -34,17 +34,17 @@
|
||||
filterable
|
||||
:disabled="inviteDialog.loading"
|
||||
style="width: 100%; margin-top: 15px">
|
||||
<el-option-group v-if="API.currentUser" :label="t('side_panel.me')">
|
||||
<el-option-group v-if="currentUser" :label="t('side_panel.me')">
|
||||
<el-option
|
||||
class="x-friend-item"
|
||||
:label="API.currentUser.displayName"
|
||||
:value="API.currentUser.id"
|
||||
:label="currentUser.displayName"
|
||||
:value="currentUser.id"
|
||||
style="height: auto">
|
||||
<div :class="['avatar', userStatusClass(API.currentUser)]">
|
||||
<img v-lazy="userImage(API.currentUser)" />
|
||||
<div :class="['avatar', userStatusClass(currentUser)]">
|
||||
<img v-lazy="userImage(currentUser)" />
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span class="name">{{ API.currentUser.displayName }}</span>
|
||||
<span class="name">{{ currentUser.displayName }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-option-group>
|
||||
@@ -156,57 +156,35 @@
|
||||
</template>
|
||||
<SendInviteDialog
|
||||
:send-invite-dialog-visible.sync="sendInviteDialogVisible"
|
||||
:invite-message-table="inviteMessageTable"
|
||||
:send-invite-dialog="sendInviteDialog"
|
||||
:invite-dialog="inviteDialog"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { instanceRequest, inviteMessagesRequest, notificationRequest } from '../../../api';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import Location from '../../Location.vue';
|
||||
import { instanceRequest, notificationRequest } from '../../../api';
|
||||
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
|
||||
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||
import SendInviteDialog from './SendInviteDialog.vue';
|
||||
|
||||
const { vipFriends, onlineFriends, activeFriends } = storeToRefs(useFriendStore());
|
||||
const { refreshInviteMessageTableData } = useInviteStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { clearInviteImageUpload } = useGalleryStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
const $confirm = instance.proxy.$confirm;
|
||||
|
||||
const userStatusClass = inject('userStatusClass');
|
||||
const userImage = inject('userImage');
|
||||
const API = inject('API');
|
||||
const clearInviteImageUpload = inject('clearInviteImageUpload');
|
||||
|
||||
const props = defineProps({
|
||||
inviteDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
vipFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
onlineFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
activeFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// SendInviteDialog
|
||||
inviteMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -229,15 +207,15 @@
|
||||
userId,
|
||||
messageSlot: {}
|
||||
};
|
||||
inviteMessagesRequest.refreshInviteMessageTableData('message');
|
||||
refreshInviteMessageTableData('message');
|
||||
clearInviteImageUpload();
|
||||
sendInviteDialogVisible.value = true;
|
||||
}
|
||||
|
||||
function addSelfToInvite() {
|
||||
const D = props.inviteDialog;
|
||||
if (!D.userIds.includes(API.currentUser.id)) {
|
||||
D.userIds.push(API.currentUser.id);
|
||||
if (!D.userIds.includes(currentUser.value.id)) {
|
||||
D.userIds.push(currentUser.value.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +251,7 @@
|
||||
const inviteLoop = () => {
|
||||
if (D.userIds.length > 0) {
|
||||
const receiverUserId = D.userIds.shift();
|
||||
if (receiverUserId === API.currentUser.id) {
|
||||
if (receiverUserId === currentUser.value.id) {
|
||||
// can't invite self!?
|
||||
const L = parseLocation(D.worldId);
|
||||
instanceRequest
|
||||
|
||||
@@ -22,18 +22,21 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { instanceRequest, notificationRequest } from '../../../api';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import { parseLocation } from '../../../shared/utils';
|
||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const API = inject('API');
|
||||
const clearInviteImageUpload = inject('clearInviteImageUpload');
|
||||
const { uploadImage } = storeToRefs(useGalleryStore());
|
||||
const { clearInviteImageUpload } = useGalleryStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -48,9 +51,6 @@
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
const inviteLoop = () => {
|
||||
if (J.userIds.length > 0) {
|
||||
const receiverUserId = J.userIds.shift();
|
||||
if (receiverUserId === API.currentUser.id) {
|
||||
if (receiverUserId === currentUser.value.id) {
|
||||
// can't invite self!?
|
||||
const L = parseLocation(J.worldId);
|
||||
instanceRequest
|
||||
@@ -78,7 +78,7 @@
|
||||
worldId: L.worldId
|
||||
})
|
||||
.finally(inviteLoop);
|
||||
} else if (props.uploadImage) {
|
||||
} else if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendInvitePhoto(
|
||||
{
|
||||
@@ -115,7 +115,7 @@
|
||||
inviteLoop();
|
||||
} else if (messageType === 'invite') {
|
||||
D.params.messageSlot = slot;
|
||||
if (props.uploadImage) {
|
||||
if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendInvitePhoto(D.params, D.userId)
|
||||
.catch((err) => {
|
||||
@@ -144,7 +144,7 @@
|
||||
}
|
||||
} else if (messageType === 'request') {
|
||||
D.params.requestSlot = slot;
|
||||
if (props.uploadImage) {
|
||||
if (uploadImage.value) {
|
||||
notificationRequest
|
||||
.sendRequestInvitePhoto(D.params, D.userId)
|
||||
.catch((err) => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
width="800px"
|
||||
append-to-body
|
||||
@close="cancelSendInvite">
|
||||
<template v-if="API.currentUser.$isVRCPlus">
|
||||
<template v-if="currentUser.$isVRCPlus">
|
||||
<!-- <template v-if="gallerySelectDialog.selectedFileId">-->
|
||||
<!-- <div style="display: inline-block; flex: none; margin-right: 5px">-->
|
||||
<!-- <el-popover placement="right" width="500px" trigger="click">-->
|
||||
@@ -70,7 +70,7 @@
|
||||
<el-button type="small" @click="cancelSendInvite">
|
||||
{{ t('dialog.invite_message.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="small" @click="API.refreshInviteMessageTableData('message')">
|
||||
<el-button type="small" @click="refreshInviteMessageTableData('message')">
|
||||
{{ t('dialog.invite_message.refresh') }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -78,37 +78,35 @@
|
||||
:visible.sync="isSendInviteConfirmDialogVisible"
|
||||
:send-invite-dialog="sendInviteDialog"
|
||||
:invite-dialog="inviteDialog"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
<EditAndSendInviteDialog
|
||||
:edit-and-send-invite-dialog.sync="editAndSendInviteDialog"
|
||||
:send-invite-dialog="sendInviteDialog"
|
||||
:invite-dialog="inviteDialog"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||
import EditAndSendInviteDialog from './EditAndSendInviteDialog.vue';
|
||||
import SendInviteConfirmDialog from './SendInviteConfirmDialog.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const API = inject('API');
|
||||
const inviteImageUpload = inject('inviteImageUpload');
|
||||
const { refreshInviteMessageTableData } = useInviteStore();
|
||||
const { inviteMessageTable } = storeToRefs(useInviteStore());
|
||||
const { inviteImageUpload } = useGalleryStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
sendInviteDialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inviteMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
sendInviteDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
@@ -117,10 +115,6 @@
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
ref="inviteGroupDialog"
|
||||
ref="inviteGroupDialogRef"
|
||||
:visible.sync="inviteGroupDialog.visible"
|
||||
:title="$t('dialog.invite_to_group.header')"
|
||||
width="450px"
|
||||
@@ -17,11 +17,11 @@
|
||||
style="margin-top: 15px"
|
||||
@change="isAllowedToInviteToGroup">
|
||||
<el-option-group
|
||||
v-if="API.currentUserGroups.size"
|
||||
v-if="currentUserGroups.size"
|
||||
:label="$t('dialog.invite_to_group.groups')"
|
||||
style="width: 410px">
|
||||
<el-option
|
||||
v-for="group in API.currentUserGroups.values()"
|
||||
v-for="group in currentUserGroups.values()"
|
||||
:key="group.id"
|
||||
:label="group.name"
|
||||
:value="group.id"
|
||||
@@ -166,132 +166,111 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, watch, getCurrentInstance, nextTick } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { groupRequest, userRequest } from '../../api';
|
||||
import { hasGroupPermission } from '../../composables/group/utils';
|
||||
import { adjustDialogZ, hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
|
||||
import { useFriendStore, useGroupStore } from '../../stores';
|
||||
|
||||
export default {
|
||||
name: 'InviteGroupDialog',
|
||||
inject: ['API', 'userStatusClass', 'userImage', 'adjustDialogZ'],
|
||||
props: {
|
||||
dialogData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
vipFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
onlineFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
activeFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
offlineFriends: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inviteGroupDialog: {
|
||||
get() {
|
||||
return this.dialogData;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:dialog-data', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'dialogData.visible'(value) {
|
||||
if (value) {
|
||||
this.initDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDialog() {
|
||||
this.$nextTick(() => this.adjustDialogZ(this.$refs.inviteGroupDialog.$el));
|
||||
const D = this.inviteGroupDialog;
|
||||
if (D.groupId) {
|
||||
this.API.getCachedGroup({
|
||||
groupId: D.groupId
|
||||
})
|
||||
.then((args) => {
|
||||
D.groupName = args.ref.name;
|
||||
})
|
||||
.catch(() => {
|
||||
D.groupId = '';
|
||||
});
|
||||
this.isAllowedToInviteToGroup();
|
||||
}
|
||||
const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
|
||||
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
|
||||
const { applyGroup } = useGroupStore();
|
||||
|
||||
if (D.userId) {
|
||||
userRequest.getCachedUser({ userId: D.userId }).then((args) => {
|
||||
D.userObject = args.ref;
|
||||
D.userIds = [D.userId];
|
||||
});
|
||||
}
|
||||
},
|
||||
isAllowedToInviteToGroup() {
|
||||
const D = this.inviteGroupDialog;
|
||||
const groupId = D.groupId;
|
||||
if (!groupId) {
|
||||
return;
|
||||
}
|
||||
this.inviteGroupDialog.loading = true;
|
||||
groupRequest
|
||||
.getGroup({ groupId })
|
||||
.then((args) => {
|
||||
if (hasGroupPermission(args.ref, 'group-invites-manage')) {
|
||||
return args;
|
||||
}
|
||||
// not allowed to invite
|
||||
this.inviteGroupDialog.groupId = '';
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: 'You are not allowed to invite to this group'
|
||||
});
|
||||
return args;
|
||||
})
|
||||
.finally(() => {
|
||||
this.inviteGroupDialog.loading = false;
|
||||
});
|
||||
},
|
||||
sendGroupInvite() {
|
||||
this.$confirm('Continue? Invite User(s) To Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
const D = this.inviteGroupDialog;
|
||||
if (action !== 'confirm' || D.loading === true) {
|
||||
return;
|
||||
}
|
||||
D.loading = true;
|
||||
const inviteLoop = () => {
|
||||
if (D.userIds.length === 0) {
|
||||
D.loading = false;
|
||||
return;
|
||||
}
|
||||
const receiverUserId = D.userIds.shift();
|
||||
groupRequest
|
||||
.sendGroupInvite({
|
||||
groupId: D.groupId,
|
||||
userId: receiverUserId
|
||||
})
|
||||
.then(inviteLoop)
|
||||
.catch(() => {
|
||||
D.loading = false;
|
||||
});
|
||||
};
|
||||
inviteLoop();
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return inviteGroupDialog.value.visible;
|
||||
},
|
||||
(value) => {
|
||||
if (value) {
|
||||
initDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const inviteGroupDialogRef = ref(null);
|
||||
|
||||
function initDialog() {
|
||||
nextTick(() => adjustDialogZ(inviteGroupDialogRef.value.$el));
|
||||
const D = inviteGroupDialog.value;
|
||||
if (D.groupId) {
|
||||
groupRequest
|
||||
.getCachedGroup({
|
||||
groupId: D.groupId
|
||||
})
|
||||
.then((args) => {
|
||||
D.groupName = args.ref.name;
|
||||
})
|
||||
.catch(() => {
|
||||
D.groupId = '';
|
||||
});
|
||||
isAllowedToInviteToGroup();
|
||||
}
|
||||
|
||||
if (D.userId) {
|
||||
userRequest.getCachedUser({ userId: D.userId }).then((args) => {
|
||||
D.userObject = args.ref;
|
||||
D.userIds = [D.userId];
|
||||
});
|
||||
}
|
||||
}
|
||||
function isAllowedToInviteToGroup() {
|
||||
const D = inviteGroupDialog.value;
|
||||
const groupId = D.groupId;
|
||||
if (!groupId) {
|
||||
return;
|
||||
}
|
||||
inviteGroupDialog.value.loading = true;
|
||||
groupRequest
|
||||
.getGroup({ groupId })
|
||||
.then((args) => {
|
||||
const ref = applyGroup(args.json);
|
||||
if (hasGroupPermission(ref, 'group-invites-manage')) {
|
||||
return args;
|
||||
}
|
||||
// not allowed to invite
|
||||
inviteGroupDialog.value.groupId = '';
|
||||
proxy.$message({
|
||||
type: 'error',
|
||||
message: 'You are not allowed to invite to this group'
|
||||
});
|
||||
return args;
|
||||
})
|
||||
.finally(() => {
|
||||
inviteGroupDialog.value.loading = false;
|
||||
});
|
||||
}
|
||||
function sendGroupInvite() {
|
||||
proxy.$confirm('Continue? Invite User(s) To Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
const D = inviteGroupDialog.value;
|
||||
if (action !== 'confirm' || D.loading === true) {
|
||||
return;
|
||||
}
|
||||
D.loading = true;
|
||||
const inviteLoop = () => {
|
||||
if (D.userIds.length === 0) {
|
||||
D.loading = false;
|
||||
return;
|
||||
}
|
||||
const receiverUserId = D.userIds.shift();
|
||||
groupRequest
|
||||
.sendGroupInvite({
|
||||
groupId: D.groupId,
|
||||
userId: receiverUserId
|
||||
})
|
||||
.then(inviteLoop)
|
||||
.catch(() => {
|
||||
D.loading = false;
|
||||
});
|
||||
};
|
||||
inviteLoop();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<safe-dialog ref="launchDialog" :visible.sync="isVisible" :title="$t('dialog.launch.header')" width="450px">
|
||||
<safe-dialog ref="launchDialogRef" :visible.sync="isVisible" :title="t('dialog.launch.header')" width="450px">
|
||||
<el-form :model="launchDialog" label-width="100px">
|
||||
<el-form-item :label="$t('dialog.launch.url')">
|
||||
<el-form-item :label="t('dialog.launch.url')">
|
||||
<el-input
|
||||
v-model="launchDialog.url"
|
||||
size="mini"
|
||||
style="width: 260px"
|
||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<el-tooltip placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-s-order"
|
||||
@@ -18,11 +18,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item v-if="launchDialog.shortUrl">
|
||||
<template slot="label">
|
||||
<span>{{ $t('dialog.launch.short_url') }}</span>
|
||||
<el-tooltip
|
||||
placement="top"
|
||||
style="margin-left: 5px"
|
||||
:content="$t('dialog.launch.short_url_notice')">
|
||||
<span>{{ t('dialog.launch.short_url') }}</span>
|
||||
<el-tooltip placement="top" style="margin-left: 5px" :content="t('dialog.launch.short_url_notice')">
|
||||
<i class="el-icon-warning" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
@@ -31,7 +28,7 @@
|
||||
size="mini"
|
||||
style="width: 260px"
|
||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<el-tooltip placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-s-order"
|
||||
@@ -40,13 +37,13 @@
|
||||
@click="copyInstanceMessage(launchDialog.shortUrl)" />
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dialog.launch.location')">
|
||||
<el-form-item :label="t('dialog.launch.location')">
|
||||
<el-input
|
||||
v-model="launchDialog.location"
|
||||
size="mini"
|
||||
style="width: 260px"
|
||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||
<el-tooltip placement="right" :content="$t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-s-order"
|
||||
@@ -57,226 +54,196 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-checkbox v-model="launchDialog.desktop" style="float: left; margin-top: 5px" @change="saveLaunchDialog">
|
||||
{{ $t('dialog.launch.start_as_desktop') }}
|
||||
{{ t('dialog.launch.start_as_desktop') }}
|
||||
</el-checkbox>
|
||||
<template slot="footer">
|
||||
<el-button size="small" @click="showPreviousInstancesInfoDialog(launchDialog.location)">
|
||||
{{ $t('dialog.launch.info') }}
|
||||
{{ t('dialog.launch.info') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
:disabled="!checkCanInvite(launchDialog.location)"
|
||||
@click="showInviteDialog(launchDialog.location)">
|
||||
{{ $t('dialog.launch.invite') }}
|
||||
{{ t('dialog.launch.invite') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!launchDialog.secureOrShortName"
|
||||
@click="launchGame(launchDialog.location, launchDialog.shortName, launchDialog.desktop)">
|
||||
{{ $t('dialog.launch.launch') }}
|
||||
@click="handleLaunchGame(launchDialog.location, launchDialog.shortName, launchDialog.desktop)">
|
||||
{{ t('dialog.launch.launch') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<InviteDialog
|
||||
:invite-dialog="inviteDialog"
|
||||
:vip-friends="vipFriends"
|
||||
:online-friends="onlineFriends"
|
||||
:active-friends="activeFriends"
|
||||
:invite-message-table="inviteMessageTable"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
<InviteDialog :invite-dialog="inviteDialog" @closeInviteDialog="closeInviteDialog" />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, nextTick, watch, getCurrentInstance, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { instanceRequest, worldRequest } from '../../api';
|
||||
import { isRealInstance, parseLocation } from '../../composables/instance/utils';
|
||||
import { getLaunchURL } from '../../composables/shared/utils';
|
||||
import configRepository from '../../service/config';
|
||||
import { adjustDialogZ, checkCanInvite, getLaunchURL, isRealInstance, parseLocation } from '../../shared/utils';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useFriendStore,
|
||||
useInstanceStore,
|
||||
useLaunchStore,
|
||||
useLocationStore
|
||||
} from '../../stores';
|
||||
import InviteDialog from './InviteDialog/InviteDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'LaunchDialog',
|
||||
components: { InviteDialog },
|
||||
inject: ['friends', 'showPreviousInstancesInfoDialog', 'adjustDialogZ'],
|
||||
props: {
|
||||
hideTooltips: Boolean,
|
||||
launchDialogData: { type: Object, required: true },
|
||||
checkCanInvite: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
vipFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
onlineFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
activeFriends: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
inviteMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
lastLocation: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
launchDialog: {
|
||||
loading: false,
|
||||
desktop: false,
|
||||
tag: '',
|
||||
location: '',
|
||||
url: '',
|
||||
shortName: '',
|
||||
shortUrl: '',
|
||||
secureOrShortName: ''
|
||||
},
|
||||
inviteDialog: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
worldId: '',
|
||||
worldName: '',
|
||||
userIds: [],
|
||||
friendsInInstance: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.launchDialogData.visible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:launch-dialog-data', { ...this.launchDialogData, visible: value });
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'launchDialogData.loading': {
|
||||
handler() {
|
||||
this.getConfig();
|
||||
this.initLaunchDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { t } = useI18n();
|
||||
|
||||
created() {
|
||||
this.getConfig();
|
||||
const { friends } = storeToRefs(useFriendStore());
|
||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { lastLocation } = storeToRefs(useLocationStore());
|
||||
const { launchGame } = useLaunchStore();
|
||||
const { launchDialogData } = storeToRefs(useLaunchStore());
|
||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||
|
||||
const launchDialogRef = ref(null);
|
||||
|
||||
const launchDialog = ref({
|
||||
loading: false,
|
||||
desktop: false,
|
||||
tag: '',
|
||||
location: '',
|
||||
url: '',
|
||||
shortName: '',
|
||||
shortUrl: '',
|
||||
secureOrShortName: ''
|
||||
});
|
||||
|
||||
const inviteDialog = ref({
|
||||
visible: false,
|
||||
loading: false,
|
||||
worldId: '',
|
||||
worldName: '',
|
||||
userIds: [],
|
||||
friendsInInstance: []
|
||||
});
|
||||
|
||||
const isVisible = computed({
|
||||
get() {
|
||||
return launchDialogData.value.visible;
|
||||
},
|
||||
methods: {
|
||||
closeInviteDialog() {
|
||||
this.inviteDialog.visible = false;
|
||||
},
|
||||
showInviteDialog(tag) {
|
||||
if (!isRealInstance(tag)) {
|
||||
return;
|
||||
}
|
||||
const L = parseLocation(tag);
|
||||
worldRequest
|
||||
.getCachedWorld({
|
||||
worldId: L.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
const D = this.inviteDialog;
|
||||
D.userIds = [];
|
||||
D.worldId = L.tag;
|
||||
D.worldName = args.ref.name;
|
||||
D.friendsInInstance = [];
|
||||
const friendsInCurrentInstance = this.lastLocation.friendList;
|
||||
for (const friend of friendsInCurrentInstance.values()) {
|
||||
const ctx = this.friends.get(friend.userId);
|
||||
if (typeof ctx.ref === 'undefined') {
|
||||
continue;
|
||||
}
|
||||
D.friendsInInstance.push(ctx);
|
||||
}
|
||||
D.visible = true;
|
||||
});
|
||||
},
|
||||
launchGame(location, shortName, desktop) {
|
||||
this.$emit('launchGame', location, shortName, desktop);
|
||||
this.isVisible = false;
|
||||
},
|
||||
getConfig() {
|
||||
configRepository.getBool('launchAsDesktop').then((value) => (this.launchDialog.desktop = value));
|
||||
},
|
||||
saveLaunchDialog() {
|
||||
configRepository.setBool('launchAsDesktop', this.launchDialog.desktop);
|
||||
},
|
||||
async initLaunchDialog() {
|
||||
const { tag, shortName } = this.launchDialogData;
|
||||
if (!isRealInstance(tag)) {
|
||||
return;
|
||||
}
|
||||
this.$nextTick(() => this.adjustDialogZ(this.$refs.launchDialog.$el));
|
||||
const D = this.launchDialog;
|
||||
D.tag = tag;
|
||||
D.secureOrShortName = shortName;
|
||||
D.shortUrl = '';
|
||||
D.shortName = shortName;
|
||||
const L = parseLocation(tag);
|
||||
L.shortName = shortName;
|
||||
if (shortName) {
|
||||
D.shortUrl = `https://vrch.at/${shortName}`;
|
||||
}
|
||||
if (L.instanceId) {
|
||||
D.location = `${L.worldId}:${L.instanceId}`;
|
||||
} else {
|
||||
D.location = L.worldId;
|
||||
}
|
||||
D.url = getLaunchURL(L);
|
||||
if (!shortName) {
|
||||
const res = await instanceRequest.getInstanceShortName({
|
||||
worldId: L.worldId,
|
||||
instanceId: L.instanceId
|
||||
});
|
||||
// NOTE:
|
||||
// splitting the 'INSTANCE:SHORTNAME' event and put code here
|
||||
if (!res.json) {
|
||||
return;
|
||||
}
|
||||
const resLocation = `${res.instance.worldId}:${res.instance.instanceId}`;
|
||||
if (resLocation === this.launchDialog.tag) {
|
||||
const resShortName = res.json.shortName;
|
||||
const secureOrShortName = res.json.shortName || res.json.secureName;
|
||||
const parsedL = parseLocation(resLocation);
|
||||
parsedL.shortName = resShortName;
|
||||
this.launchDialog.shortName = resShortName;
|
||||
this.launchDialog.secureOrShortName = secureOrShortName;
|
||||
if (resShortName) {
|
||||
this.launchDialog.shortUrl = `https://vrch.at/${resShortName}`;
|
||||
}
|
||||
this.launchDialog.url = getLaunchURL(parsedL);
|
||||
}
|
||||
}
|
||||
},
|
||||
async copyInstanceMessage(input) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(input);
|
||||
this.$message({
|
||||
message: 'Instance copied to clipboard',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
this.$message({
|
||||
message: 'Instance copied failed',
|
||||
type: 'error'
|
||||
});
|
||||
console.error(error.message);
|
||||
}
|
||||
set(value) {
|
||||
launchDialogData.value.visible = value;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => launchDialogData.value.loading,
|
||||
(loading) => {
|
||||
if (loading) {
|
||||
getConfig();
|
||||
initLaunchDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
getConfig();
|
||||
|
||||
function closeInviteDialog() {
|
||||
inviteDialog.value.visible = false;
|
||||
}
|
||||
function showInviteDialog(tag) {
|
||||
if (!isRealInstance(tag)) {
|
||||
return;
|
||||
}
|
||||
const L = parseLocation(tag);
|
||||
worldRequest
|
||||
.getCachedWorld({
|
||||
worldId: L.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
const D = inviteDialog.value;
|
||||
D.userIds = [];
|
||||
D.worldId = L.tag;
|
||||
D.worldName = args.ref.name;
|
||||
D.friendsInInstance = [];
|
||||
const friendsInCurrentInstance = lastLocation.value.friendList;
|
||||
for (const friend of friendsInCurrentInstance.values()) {
|
||||
const ctx = friends.value.get(friend.userId);
|
||||
if (typeof ctx.ref === 'undefined') {
|
||||
continue;
|
||||
}
|
||||
D.friendsInInstance.push(ctx);
|
||||
}
|
||||
D.visible = true;
|
||||
});
|
||||
}
|
||||
function handleLaunchGame(location, shortName, desktop) {
|
||||
launchGame(location, shortName, desktop);
|
||||
isVisible.value = false;
|
||||
}
|
||||
function getConfig() {
|
||||
configRepository.getBool('launchAsDesktop').then((value) => (launchDialog.value.desktop = value));
|
||||
}
|
||||
function saveLaunchDialog() {
|
||||
configRepository.setBool('launchAsDesktop', launchDialog.value.desktop);
|
||||
}
|
||||
async function initLaunchDialog() {
|
||||
const { tag, shortName } = launchDialogData.value;
|
||||
if (!isRealInstance(tag)) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => adjustDialogZ(launchDialogRef.value.$el));
|
||||
const D = launchDialog.value;
|
||||
D.tag = tag;
|
||||
D.secureOrShortName = shortName;
|
||||
D.shortUrl = '';
|
||||
D.shortName = shortName;
|
||||
const L = parseLocation(tag);
|
||||
L.shortName = shortName;
|
||||
if (shortName) {
|
||||
D.shortUrl = `https://vrch.at/${shortName}`;
|
||||
}
|
||||
if (L.instanceId) {
|
||||
D.location = `${L.worldId}:${L.instanceId}`;
|
||||
} else {
|
||||
D.location = L.worldId;
|
||||
}
|
||||
D.url = getLaunchURL(L);
|
||||
if (!shortName) {
|
||||
const res = await instanceRequest.getInstanceShortName({
|
||||
worldId: L.worldId,
|
||||
instanceId: L.instanceId
|
||||
});
|
||||
if (!res.json) {
|
||||
return;
|
||||
}
|
||||
const resLocation = `${res.instance.worldId}:${res.instance.instanceId}`;
|
||||
if (resLocation === launchDialog.value.tag) {
|
||||
const resShortName = res.json.shortName;
|
||||
const secureOrShortName = res.json.shortName || res.json.secureName;
|
||||
const parsedL = parseLocation(resLocation);
|
||||
parsedL.shortName = resShortName;
|
||||
launchDialog.value.shortName = resShortName;
|
||||
launchDialog.value.secureOrShortName = secureOrShortName;
|
||||
if (resShortName) {
|
||||
launchDialog.value.shortUrl = `https://vrch.at/${resShortName}`;
|
||||
}
|
||||
launchDialog.value.url = getLaunchURL(parsedL);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function copyInstanceMessage(input) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(input);
|
||||
proxy.$message({
|
||||
message: 'Instance copied to clipboard',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
proxy.$message({
|
||||
message: 'Instance copied failed',
|
||||
type: 'error'
|
||||
});
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,26 +26,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { useGalleryStore } from '../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const showFullscreenImageDialog = inject('showFullscreenImageDialog');
|
||||
|
||||
defineProps({
|
||||
previousImagesDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
previousImagesTable: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:previousImagesDialogVisible']);
|
||||
const { previousImagesDialogVisible, previousImagesTable } = storeToRefs(useGalleryStore());
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
|
||||
function closeDialog() {
|
||||
emit('update:previousImagesDialogVisible', false);
|
||||
previousImagesDialogVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
ref="dialog"
|
||||
:visible="visible"
|
||||
ref="dialogRef"
|
||||
:visible="previousInstancesInfoDialogVisible"
|
||||
:title="$t('dialog.previous_instances.info')"
|
||||
width="800px"
|
||||
:fullscreen="fullscreen"
|
||||
destroy-on-close
|
||||
@close="$emit('update:visible', false)">
|
||||
@close="closeDialog">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<location :location="location.tag" style="font-size: 14px"></location>
|
||||
<Location :location="location.tag" style="font-size: 14px" />
|
||||
<el-input
|
||||
v-model="dataTable.filters[0].value"
|
||||
:placeholder="$t('dialog.previous_instances.search_placeholder')"
|
||||
@@ -20,9 +20,9 @@
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip placement="left">
|
||||
<template slot="content">
|
||||
<span>{{ scope.row.created_at | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||
</template>
|
||||
<span>{{ scope.row.created_at | formatDate('short') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.created_at, 'short') }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -57,105 +57,85 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dayjs from 'dayjs';
|
||||
import utils from '../../../classes/utils';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import database from '../../../service/database';
|
||||
import Location from '../../Location.vue';
|
||||
<script setup>
|
||||
import { ref, watch, nextTick } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { database } from '../../../service/database';
|
||||
import {
|
||||
adjustDialogZ,
|
||||
compareByCreatedAt,
|
||||
parseLocation,
|
||||
timeToText,
|
||||
formatDateFilter
|
||||
} from '../../../shared/utils';
|
||||
import { useGameLogStore, useInstanceStore, useUserStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
name: 'PreviousInstancesInfoDialog',
|
||||
components: {
|
||||
Location
|
||||
},
|
||||
inject: ['adjustDialogZ'],
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
instanceId: { type: String, required: true },
|
||||
gameLogIsFriend: { type: Function, required: true },
|
||||
gameLogIsFavorite: { type: Function, required: true },
|
||||
lookupUser: { type: Function, required: true },
|
||||
isDarkMode: { type: Boolean, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
echarts: null,
|
||||
echartsInstance: null,
|
||||
loading: false,
|
||||
location: {},
|
||||
currentTab: 'table',
|
||||
dataTable: {
|
||||
data: [],
|
||||
filters: [
|
||||
{
|
||||
prop: 'displayName',
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
},
|
||||
fullscreen: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
activityDetailData() {
|
||||
return this.dataTable.data.map((item) => ({
|
||||
displayName: item.displayName,
|
||||
joinTime: dayjs(item.created_at),
|
||||
leaveTime: dayjs(item.created_at).add(item.time, 'ms'),
|
||||
time: item.time,
|
||||
timer: item.timer
|
||||
}));
|
||||
const { lookupUser } = useUserStore();
|
||||
const { previousInstancesInfoDialogVisible, previousInstancesInfoDialogInstanceId } =
|
||||
storeToRefs(useInstanceStore());
|
||||
const { gameLogIsFriend, gameLogIsFavorite } = useGameLogStore();
|
||||
|
||||
const dialogRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const location = ref({});
|
||||
const dataTable = ref({
|
||||
data: [],
|
||||
filters: [
|
||||
{
|
||||
prop: 'displayName',
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(value) {
|
||||
if (value) {
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
this.refreshPreviousInstancesInfoTable();
|
||||
});
|
||||
utils.loadEcharts().then((echarts) => {
|
||||
this.echarts = echarts;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.adjustDialogZ(this.$refs.dialog.$el);
|
||||
this.loading = true;
|
||||
this.location = parseLocation(this.instanceId);
|
||||
},
|
||||
refreshPreviousInstancesInfoTable() {
|
||||
database.getPlayersFromInstance(this.location.tag).then((data) => {
|
||||
const array = [];
|
||||
for (const entry of Array.from(data.values())) {
|
||||
entry.timer = utils.timeToText(entry.time);
|
||||
array.push(entry);
|
||||
}
|
||||
array.sort(utils.compareByCreatedAt);
|
||||
this.dataTable.data = array;
|
||||
this.loading = false;
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const fullscreen = ref(false);
|
||||
|
||||
watch(
|
||||
() => previousInstancesInfoDialogVisible.value,
|
||||
(value) => {
|
||||
if (value) {
|
||||
nextTick(() => {
|
||||
init();
|
||||
refreshPreviousInstancesInfoTable();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function init() {
|
||||
adjustDialogZ(dialogRef.value.$el);
|
||||
loading.value = true;
|
||||
location.value = parseLocation(previousInstancesInfoDialogInstanceId.value);
|
||||
}
|
||||
|
||||
function refreshPreviousInstancesInfoTable() {
|
||||
database.getPlayersFromInstance(location.value.tag).then((data) => {
|
||||
const array = [];
|
||||
for (const entry of Array.from(data.values())) {
|
||||
entry.timer = timeToText(entry.time);
|
||||
array.push(entry);
|
||||
}
|
||||
array.sort(compareByCreatedAt);
|
||||
dataTable.value.data = array;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
previousInstancesInfoDialogVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,45 +2,44 @@
|
||||
<safe-dialog
|
||||
ref="previousInstancesWorldDialog"
|
||||
:visible.sync="isVisible"
|
||||
:title="$t('dialog.previous_instances.header')"
|
||||
:title="t('dialog.previous_instances.header')"
|
||||
width="1000px"
|
||||
append-to-body>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<span style="font-size: 14px" v-text="previousInstancesWorldDialog.worldRef.name"></span>
|
||||
<el-input
|
||||
v-model="previousInstancesWorldDialogTable.filters[0].value"
|
||||
:placeholder="$t('dialog.previous_instances.search_placeholder')"
|
||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||
style="display: block; width: 150px"></el-input>
|
||||
</div>
|
||||
<data-tables v-loading="loading" v-bind="previousInstancesWorldDialogTable" style="margin-top: 10px">
|
||||
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.created_at | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.instance_name')" prop="name">
|
||||
<el-table-column :label="t('table.previous_instances.instance_name')" prop="name">
|
||||
<template slot-scope="scope">
|
||||
<location-world
|
||||
<LocationWorld
|
||||
:locationobject="scope.row.$location"
|
||||
:grouphint="scope.row.groupName"
|
||||
:currentuserid="API.currentUser.id"
|
||||
@show-launch-dialog="showLaunchDialog"></location-world>
|
||||
:currentuserid="currentUser.id" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.instance_creator')" prop="location">
|
||||
<el-table-column :label="t('table.previous_instances.instance_creator')" prop="location">
|
||||
<template slot-scope="scope">
|
||||
<display-name
|
||||
<DisplayName
|
||||
:userid="scope.row.$location.userId"
|
||||
:location="scope.row.$location.tag"
|
||||
:force-update-key="previousInstancesWorldDialog.forceUpdate"></display-name>
|
||||
:force-update-key="previousInstancesWorldDialog.forceUpdate" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
|
||||
<el-table-column :label="t('table.previous_instances.time')" prop="time" width="100" sortable>
|
||||
<template slot-scope="scope">
|
||||
<span v-text="scope.row.timer"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.action')" width="90" align="right">
|
||||
<el-table-column :label="t('table.previous_instances.action')" width="90" align="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
@@ -66,111 +65,106 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from '../../../classes/utils';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import database from '../../../service/database';
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { database } from '../../../service/database';
|
||||
import {
|
||||
adjustDialogZ,
|
||||
compareByCreatedAt,
|
||||
parseLocation,
|
||||
removeFromArray,
|
||||
timeToText,
|
||||
formatDateFilter
|
||||
} from '../../../shared/utils';
|
||||
import { useInstanceStore, useUiStore, useUserStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
name: 'PreviousInstancesWorldDialog',
|
||||
inject: ['API', 'showLaunchDialog', 'showPreviousInstancesInfoDialog', 'adjustDialogZ'],
|
||||
props: {
|
||||
previousInstancesWorldDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
shiftHeld: Boolean
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
previousInstancesWorldDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['update:previous-instances-world-dialog']);
|
||||
|
||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const previousInstancesWorldDialogTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: 'groupName', value: '' }],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: { prop: 'created_at', order: 'descending' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previousInstancesWorldDialogTable: {
|
||||
data: [],
|
||||
filters: [
|
||||
{
|
||||
prop: 'groupName',
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
},
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.previousInstancesWorldDialog.visible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:previous-instances-world-dialog', {
|
||||
...this.previousInstancesWorldDialog,
|
||||
visible: value
|
||||
});
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const loading = ref(false);
|
||||
|
||||
const isVisible = computed({
|
||||
get: () => props.previousInstancesWorldDialog.visible,
|
||||
set: (value) => {
|
||||
emit('update:previous-instances-world-dialog', {
|
||||
...props.previousInstancesWorldDialog,
|
||||
visible: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function refreshPreviousInstancesWorldTable() {
|
||||
loading.value = true;
|
||||
const D = props.previousInstancesWorldDialog;
|
||||
database.getPreviousInstancesByWorldId(D.worldRef).then((data) => {
|
||||
const array = [];
|
||||
for (const ref of data.values()) {
|
||||
ref.$location = parseLocation(ref.location);
|
||||
ref.timer = ref.time > 0 ? timeToText(ref.time) : '';
|
||||
array.push(ref);
|
||||
}
|
||||
array.sort(compareByCreatedAt);
|
||||
previousInstancesWorldDialogTable.data = array;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function deleteGameLogWorldInstance(row) {
|
||||
database.deleteGameLogInstanceByInstanceId({ location: row.location });
|
||||
removeFromArray(previousInstancesWorldDialogTable.data, row);
|
||||
}
|
||||
|
||||
function deleteGameLogWorldInstancePrompt(row) {
|
||||
proxy.$confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteGameLogWorldInstance(row);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'previousInstancesWorldDialog.openFlg'() {
|
||||
if (this.previousInstancesWorldDialog.visible) {
|
||||
this.$nextTick(() => {
|
||||
this.adjustDialogZ(this.$refs.previousInstancesWorldDialog.$el);
|
||||
});
|
||||
this.refreshPreviousInstancesWorldTable();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshPreviousInstancesWorldTable() {
|
||||
this.loading = true;
|
||||
const D = this.previousInstancesWorldDialog;
|
||||
database.getpreviousInstancesByWorldId(D.worldRef).then((data) => {
|
||||
const array = [];
|
||||
for (const ref of data.values()) {
|
||||
ref.$location = parseLocation(ref.location);
|
||||
if (ref.time > 0) {
|
||||
ref.timer = utils.timeToText(ref.time);
|
||||
} else {
|
||||
ref.timer = '';
|
||||
}
|
||||
array.push(ref);
|
||||
}
|
||||
array.sort(utils.compareByCreatedAt);
|
||||
this.previousInstancesWorldDialogTable.data = array;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
deleteGameLogWorldInstance(row) {
|
||||
database.deleteGameLogInstanceByInstanceId({
|
||||
location: row.location
|
||||
});
|
||||
utils.removeFromArray(this.previousInstancesWorldDialogTable.data, row);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
deleteGameLogWorldInstancePrompt(row) {
|
||||
this.$confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.deleteGameLogWorldInstance(row);
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => props.previousInstancesWorldDialog.openFlg,
|
||||
() => {
|
||||
if (props.previousInstancesWorldDialog.visible) {
|
||||
nextTick(() => {
|
||||
adjustDialogZ(proxy.$refs.previousInstancesWorldDialog.$el);
|
||||
});
|
||||
refreshPreviousInstancesWorldTable();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<el-input
|
||||
v-for="(link, index) in bioDialog.bioLinks"
|
||||
:key="index"
|
||||
v-model="bioDialog.bioLinks[index]"
|
||||
:value="link"
|
||||
size="small"
|
||||
style="margin-top: 5px">
|
||||
@@ -52,7 +51,7 @@
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { userRequest } from '../../../api';
|
||||
import { getFaviconUrl } from '../../../composables/shared/utils';
|
||||
import { getFaviconUrl } from '../../../shared/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { $message } = getCurrentInstance().proxy;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
width="400px"
|
||||
append-to-body>
|
||||
<div v-loading="languageDialog.loading">
|
||||
<div v-for="item in API.currentUser.$languages" :key="item.key" style="margin: 6px 0">
|
||||
<div v-for="item in currentUser.$languages" :key="item.key" style="margin: 6px 0">
|
||||
<el-tag
|
||||
size="small"
|
||||
type="info"
|
||||
@@ -23,9 +23,7 @@
|
||||
</div>
|
||||
<el-select
|
||||
value=""
|
||||
:disabled="
|
||||
languageDialog.loading || (API.currentUser.$languages && API.currentUser.$languages.length === 3)
|
||||
"
|
||||
:disabled="languageDialog.loading || (currentUser.$languages && currentUser.$languages.length === 3)"
|
||||
:placeholder="t('dialog.language.select_language')"
|
||||
style="margin-top: 14px"
|
||||
@change="addUserLanguage">
|
||||
@@ -46,29 +44,21 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { userRequest } from '../../../api';
|
||||
|
||||
import { languageClass } from '../../../composables/user/utils';
|
||||
import { languageClass } from '../../../shared/utils';
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const API = inject('API');
|
||||
|
||||
const props = defineProps({
|
||||
languageDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const { languageDialog, currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
function removeUserLanguage(language) {
|
||||
if (language !== String(language)) {
|
||||
return;
|
||||
}
|
||||
const D = props.languageDialog;
|
||||
const D = languageDialog.value;
|
||||
D.loading = true;
|
||||
userRequest
|
||||
.removeUserTags({
|
||||
@@ -83,7 +73,7 @@
|
||||
if (language !== String(language)) {
|
||||
return;
|
||||
}
|
||||
const D = props.languageDialog;
|
||||
const D = languageDialog.value;
|
||||
D.loading = true;
|
||||
userRequest
|
||||
.addUserTags({
|
||||
|
||||
@@ -15,22 +15,20 @@
|
||||
<data-tables v-loading="loading" v-bind="previousInstancesUserDialogTable" style="margin-top: 10px">
|
||||
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.created_at | formatDate('long') }}</span>
|
||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.world')" prop="name" sortable>
|
||||
<template slot-scope="scope">
|
||||
<location
|
||||
<Location
|
||||
:location="scope.row.location"
|
||||
:hint="scope.row.worldName"
|
||||
:grouphint="scope.row.groupName"></location>
|
||||
:grouphint="scope.row.groupName" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.instance_creator')" prop="location" width="170">
|
||||
<template slot-scope="scope">
|
||||
<display-name
|
||||
:userid="scope.row.$location.userId"
|
||||
:location="scope.row.$location.tag"></display-name>
|
||||
<DisplayName :userid="scope.row.$location.userId" :location="scope.row.$location.tag" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
|
||||
@@ -69,138 +67,115 @@
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import utils from '../../../classes/utils';
|
||||
import { parseLocation } from '../../../composables/instance/utils';
|
||||
import database from '../../../service/database';
|
||||
import Location from '../../Location.vue';
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { database } from '../../../service/database';
|
||||
import {
|
||||
adjustDialogZ,
|
||||
compareByCreatedAt,
|
||||
parseLocation,
|
||||
removeFromArray,
|
||||
timeToText,
|
||||
formatDateFilter
|
||||
} from '../../../shared/utils';
|
||||
import { useInstanceStore, useLaunchStore, useUiStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
name: 'PreviousInstancesUserDialog',
|
||||
components: {
|
||||
Location
|
||||
},
|
||||
inject: ['adjustDialogZ', 'showLaunchDialog', 'showPreviousInstancesInfoDialog'],
|
||||
props: {
|
||||
previousInstancesUserDialog: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
userRef: {},
|
||||
loading: false,
|
||||
forceUpdate: 0,
|
||||
previousInstances: [],
|
||||
previousInstancesTable: {
|
||||
data: [],
|
||||
filters: [
|
||||
{
|
||||
prop: 'displayName',
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
height: '400px'
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
shiftHeld: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previousInstancesUserDialogTable: {
|
||||
const props = defineProps({
|
||||
previousInstancesUserDialog: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
visible: false,
|
||||
userRef: {},
|
||||
loading: false,
|
||||
forceUpdate: 0,
|
||||
previousInstances: [],
|
||||
previousInstancesTable: {
|
||||
data: [],
|
||||
filters: [
|
||||
{
|
||||
prop: 'worldName',
|
||||
value: ''
|
||||
}
|
||||
],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: {
|
||||
prop: 'created_at',
|
||||
order: 'descending'
|
||||
}
|
||||
},
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
},
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.previousInstancesUserDialog.visible;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:previous-instances-user-dialog', {
|
||||
...this.previousInstancesUserDialog,
|
||||
visible: value
|
||||
});
|
||||
filters: [{ prop: 'displayName', value: '' }],
|
||||
tableProps: { stripe: true, size: 'mini', height: '400px' }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:previous-instances-user-dialog']);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const loading = ref(false);
|
||||
const previousInstancesUserDialogTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: 'worldName', value: '' }],
|
||||
tableProps: {
|
||||
stripe: true,
|
||||
size: 'mini',
|
||||
defaultSort: { prop: 'created_at', order: 'descending' }
|
||||
},
|
||||
watch: {
|
||||
'previousInstancesUserDialog.openFlg'() {
|
||||
if (this.previousInstancesUserDialog.visible) {
|
||||
this.$nextTick(() => {
|
||||
this.adjustDialogZ(this.$refs.previousInstancesUserDialog.$el);
|
||||
});
|
||||
this.refreshPreviousInstancesUserTable();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshPreviousInstancesUserTable() {
|
||||
this.loading = true;
|
||||
database.getpreviousInstancesByUserId(this.previousInstancesUserDialog.userRef).then((data) => {
|
||||
const array = [];
|
||||
for (const ref of data.values()) {
|
||||
ref.$location = parseLocation(ref.location);
|
||||
if (ref.time > 0) {
|
||||
ref.timer = utils.timeToText(ref.time);
|
||||
} else {
|
||||
ref.timer = '';
|
||||
}
|
||||
array.push(ref);
|
||||
}
|
||||
array.sort(utils.compareByCreatedAt);
|
||||
this.previousInstancesUserDialogTable.data = array;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
deleteGameLogUserInstance(row) {
|
||||
database.deleteGameLogInstance({
|
||||
id: this.previousInstancesUserDialog.userRef.id,
|
||||
displayName: this.previousInstancesUserDialog.userRef.displayName,
|
||||
location: row.location
|
||||
});
|
||||
utils.removeFromArray(this.previousInstancesUserDialogTable.data, row);
|
||||
},
|
||||
deleteGameLogUserInstancePrompt(row) {
|
||||
this.$confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.deleteGameLogUserInstance(row);
|
||||
}
|
||||
}
|
||||
pageSize: 10,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
|
||||
const { showLaunchDialog } = useLaunchStore();
|
||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||
const { shiftHeld } = storeToRefs(useUiStore());
|
||||
|
||||
const isVisible = computed({
|
||||
get: () => props.previousInstancesUserDialog.visible,
|
||||
set: (value) => {
|
||||
emit('update:previous-instances-user-dialog', {
|
||||
...props.previousInstancesUserDialog,
|
||||
visible: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const refreshPreviousInstancesUserTable = async () => {
|
||||
loading.value = true;
|
||||
const data = await database.getPreviousInstancesByUserId(props.previousInstancesUserDialog.userRef);
|
||||
const array = [];
|
||||
for (const item of data.values()) {
|
||||
item.$location = parseLocation(item.location);
|
||||
item.timer = item.time > 0 ? timeToText(item.time) : '';
|
||||
array.push(item);
|
||||
}
|
||||
array.sort(compareByCreatedAt);
|
||||
previousInstancesUserDialogTable.data = array;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.previousInstancesUserDialog.openFlg,
|
||||
() => {
|
||||
if (props.previousInstancesUserDialog.visible) {
|
||||
nextTick(() => {
|
||||
adjustDialogZ(proxy.$refs.previousInstancesUserDialog.$el);
|
||||
});
|
||||
refreshPreviousInstancesUserTable();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function deleteGameLogUserInstance(row) {
|
||||
database.deleteGameLogInstance({
|
||||
id: props.previousInstancesUserDialog.userRef.id,
|
||||
displayName: props.previousInstancesUserDialog.userRef.displayName,
|
||||
location: row.location
|
||||
});
|
||||
removeFromArray(previousInstancesUserDialogTable.data, row);
|
||||
}
|
||||
|
||||
function deleteGameLogUserInstancePrompt(row) {
|
||||
proxy.$confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') deleteGameLogUserInstance(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
width="800px"
|
||||
append-to-body
|
||||
@close="cancelSendInviteRequest">
|
||||
<template v-if="API.currentUser.$isVRCPlus">
|
||||
<template v-if="currentUser.$isVRCPlus">
|
||||
<input class="inviteImageUploadButton" type="file" accept="image/*" @change="inviteImageUpload" />
|
||||
</template>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<el-button type="small" @click="cancelSendInviteRequest">{{
|
||||
t('dialog.invite_request_message.cancel')
|
||||
}}</el-button>
|
||||
<el-button type="small" @click="API.refreshInviteMessageTableData('request')">{{
|
||||
<el-button type="small" @click="refreshInviteMessageTableData('request')">{{
|
||||
t('dialog.invite_request_message.refresh')
|
||||
}}</el-button>
|
||||
</template>
|
||||
@@ -53,37 +53,37 @@
|
||||
:visible.sync="isSendInviteConfirmDialogVisible"
|
||||
:send-invite-dialog="sendInviteDialog"
|
||||
:invite-dialog="inviteDialog"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
<EditAndSendInviteDialog
|
||||
:edit-and-send-invite-dialog.sync="editAndSendInviteDialog"
|
||||
:send-invite-dialog="sendInviteDialog"
|
||||
:invite-dialog="inviteDialog"
|
||||
:upload-image="uploadImage"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||
import EditAndSendInviteDialog from '../InviteDialog/EditAndSendInviteDialog.vue';
|
||||
import SendInviteConfirmDialog from '../InviteDialog/SendInviteConfirmDialog.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const API = inject('API');
|
||||
const inviteImageUpload = inject('inviteImageUpload');
|
||||
const inviteStore = useInviteStore();
|
||||
const { refreshInviteMessageTableData } = inviteStore;
|
||||
const { inviteRequestMessageTable } = storeToRefs(inviteStore);
|
||||
const galleryStore = useGalleryStore();
|
||||
const { inviteImageUpload } = galleryStore;
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
sendInviteRequestDialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inviteRequestMessageTable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
sendInviteDialog: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
@@ -92,10 +92,6 @@
|
||||
type: Object,
|
||||
require: false,
|
||||
default: () => ({})
|
||||
},
|
||||
uploadImage: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -120,7 +116,6 @@
|
||||
visible: true
|
||||
};
|
||||
}
|
||||
|
||||
function cancelSendInviteRequest() {
|
||||
emit('update:sendInviteRequestDialogVisible', false);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<el-option :label="t('dialog.user.status.busy')" value="busy">
|
||||
<i class="x-user-status busy"></i> {{ t('dialog.user.status.busy') }}
|
||||
</el-option>
|
||||
<el-option v-if="API.currentUser.$isModerator" :label="t('dialog.user.status.offline')" value="offline">
|
||||
<el-option v-if="currentUser.$isModerator" :label="t('dialog.user.status.offline')" value="offline">
|
||||
<i class="x-user-status offline"></i> {{ t('dialog.user.status.offline') }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
@@ -44,6 +44,7 @@
|
||||
:placeholder="t('dialog.social_status.status_placeholder')"
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
clearable
|
||||
style="display: block; margin-top: 10px"></el-input>
|
||||
</div>
|
||||
|
||||
@@ -56,13 +57,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, getCurrentInstance } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { userRequest } from '../../../api';
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { $message } = getCurrentInstance().proxy;
|
||||
const API = inject('API');
|
||||
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const props = defineProps({
|
||||
socialStatusDialog: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
class="x-dialog"
|
||||
:visible.sync="VRCXUpdateDialog.visible"
|
||||
:title="t('dialog.vrcx_updater.header')"
|
||||
append-to-body
|
||||
width="400px">
|
||||
<div v-loading="checkingForVRCXUpdate" style="margin-top: 15px">
|
||||
<template v-if="updateInProgress">
|
||||
@@ -17,11 +18,10 @@
|
||||
<span>{{ t('dialog.vrcx_updater.ready_for_update') }}</span>
|
||||
</div>
|
||||
<el-select
|
||||
v-model="currentBranch"
|
||||
v-model="branch"
|
||||
style="display: inline-block; width: 150px; margin-right: 15px"
|
||||
@change="loadBranchVersions">
|
||||
<el-option v-for="branch in branches" :key="branch.name" :label="branch.name" :value="branch.name">
|
||||
</el-option>
|
||||
<el-option v-for="b in branches" :key="b.name" :label="b.name" :value="b.name"> </el-option>
|
||||
</el-select>
|
||||
<el-select v-model="VRCXUpdateDialog.release" style="display: inline-block; width: 150px">
|
||||
<el-option
|
||||
@@ -63,72 +63,32 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, inject, watch, nextTick } from 'vue';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { branches } from '../../shared/constants';
|
||||
import { adjustDialogZ } from '../../shared/utils';
|
||||
import { useVRCXUpdaterStore } from '../../stores';
|
||||
|
||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||
|
||||
const {
|
||||
appVersion,
|
||||
branch,
|
||||
checkingForVRCXUpdate,
|
||||
VRCXUpdateDialog,
|
||||
pendingVRCXInstall,
|
||||
updateInProgress,
|
||||
updateProgress
|
||||
} = storeToRefs(VRCXUpdaterStore);
|
||||
const { installVRCXUpdate, loadBranchVersions, restartVRCX, updateProgressText, cancelUpdate } = VRCXUpdaterStore;
|
||||
|
||||
const { t } = useI18n();
|
||||
const adjustDialogZ = inject('adjustDialogZ');
|
||||
|
||||
const props = defineProps({
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
VRCXUpdateDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
appVersion: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
checkingForVRCXUpdate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
updateInProgress: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
updateProgress: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
updateProgressText: {
|
||||
type: Function,
|
||||
default: () => ''
|
||||
},
|
||||
pendingVRCXInstall: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
branch: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
branches: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const VRCXUpdateDialogRef = ref(null);
|
||||
|
||||
const emit = defineEmits([
|
||||
'loadBranchVersions',
|
||||
'cancelUpdate',
|
||||
'installVRCXUpdate',
|
||||
'restartVRCX',
|
||||
'update:branch'
|
||||
]);
|
||||
|
||||
const currentBranch = computed({
|
||||
get: () => props.branch,
|
||||
set: (value) => {
|
||||
emit('update:branch', value);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.VRCXUpdateDialog,
|
||||
() => VRCXUpdateDialog,
|
||||
(newVal) => {
|
||||
if (newVal.visible) {
|
||||
nextTick(() => {
|
||||
@@ -137,20 +97,4 @@
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function loadBranchVersions(event) {
|
||||
emit('loadBranchVersions', event);
|
||||
}
|
||||
|
||||
function cancelUpdate() {
|
||||
emit('cancelUpdate');
|
||||
}
|
||||
|
||||
function installVRCXUpdate() {
|
||||
emit('installVRCXUpdate');
|
||||
}
|
||||
|
||||
function restartVRCX(isUpgrade) {
|
||||
emit('restartVRCX', isUpgrade);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -25,12 +25,9 @@
|
||||
<!-- el-button(type="default" size="small" @click="deleteWorldImage" icon="el-icon-delete") Delete Latest Image-->
|
||||
</el-button-group>
|
||||
<br />
|
||||
<div
|
||||
v-for="image in previousImagesTable"
|
||||
v-if="image.file"
|
||||
:key="image.version"
|
||||
style="display: inline-block">
|
||||
<div v-for="image in previousImagesTable" :key="image.version" style="display: inline-block">
|
||||
<div
|
||||
v-if="image.file"
|
||||
class="x-change-image-item"
|
||||
style="cursor: pointer"
|
||||
:class="{ 'current-image': compareCurrentImage(image) }"
|
||||
@@ -43,35 +40,31 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, inject, ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getCurrentInstance, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { imageRequest } from '../../../api';
|
||||
import { extractFileId } from '../../../composables/shared/utils';
|
||||
import webApiService from '../../../service/webapi';
|
||||
import { AppGlobal } from '../../../service/appConfig';
|
||||
import { $throw } from '../../../service/request';
|
||||
import { extractFileId } from '../../../shared/utils';
|
||||
import { useGalleryStore, useWorldStore } from '../../../stores';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const API = inject('API');
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
const $message = instance.proxy.$message;
|
||||
|
||||
const { worldDialog } = storeToRefs(useWorldStore());
|
||||
const { previousImagesTable } = storeToRefs(useGalleryStore());
|
||||
|
||||
const props = defineProps({
|
||||
changeWorldImageDialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
previousImagesTable: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
previousImagesFileId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
worldDialog: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
@@ -126,7 +119,7 @@
|
||||
}
|
||||
};
|
||||
const files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length || !props.worldDialog.visible || props.worldDialog.loading) {
|
||||
if (!files.length || !worldDialog.value.visible || worldDialog.value.loading) {
|
||||
clearFile();
|
||||
return;
|
||||
}
|
||||
@@ -158,8 +151,8 @@
|
||||
const base64SignatureFile = await genSig(base64File);
|
||||
const signatureMd5 = await genMd5(base64SignatureFile);
|
||||
const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10);
|
||||
const worldId = props.worldDialog.id;
|
||||
const { imageUrl } = props.worldDialog.ref;
|
||||
const worldId = worldDialog.value.id;
|
||||
const { imageUrl } = worldDialog.value.ref;
|
||||
const fileId = extractFileId(imageUrl);
|
||||
if (!fileId) {
|
||||
$message({
|
||||
@@ -204,7 +197,6 @@
|
||||
}
|
||||
|
||||
async function worldImageInit(args) {
|
||||
// API.$on('WORLDIMAGE:INIT')
|
||||
const fileId = args.json.id;
|
||||
const fileVersion = args.json.versions[args.json.versions.length - 1].version;
|
||||
const params = {
|
||||
@@ -216,7 +208,6 @@
|
||||
}
|
||||
|
||||
async function worldImageFileStart(args) {
|
||||
// API.$on('WORLDIMAGE:FILESTART')
|
||||
const { url } = args.json;
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
@@ -239,9 +230,8 @@
|
||||
});
|
||||
|
||||
if (json.status !== 200) {
|
||||
// $app.worldDialog.loading = false;
|
||||
changeWorldImageDialogLoading.value = false;
|
||||
API.$throw('World image upload failed', json, params.url);
|
||||
$throw('World image upload failed', json, params.url);
|
||||
}
|
||||
const args = {
|
||||
json,
|
||||
@@ -251,7 +241,6 @@
|
||||
}
|
||||
|
||||
async function worldImageFileAWS(args) {
|
||||
// API.$on('WORLDIMAGE:FILEAWS')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -262,7 +251,6 @@
|
||||
}
|
||||
|
||||
async function worldImageFileFinish(args) {
|
||||
// API.$on('WORLDIMAGE:FILEFINISH')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -273,7 +261,6 @@
|
||||
}
|
||||
|
||||
async function worldImageSigStart(args) {
|
||||
// API.$on('WORLDIMAGE:SIGSTART')
|
||||
const { url } = args.json;
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
@@ -296,9 +283,8 @@
|
||||
});
|
||||
|
||||
if (json.status !== 200) {
|
||||
// $app.worldDialog.loading = false;
|
||||
changeWorldImageDialogLoading.value = false;
|
||||
API.$throw('World image upload failed', json, params.url);
|
||||
$throw('World image upload failed', json, params.url);
|
||||
}
|
||||
const args = {
|
||||
json,
|
||||
@@ -308,7 +294,6 @@
|
||||
}
|
||||
|
||||
async function worldImageSigAWS(args) {
|
||||
// API.$on('WORLDIMAGE:SIGAWS')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const params = {
|
||||
fileId,
|
||||
@@ -318,11 +303,10 @@
|
||||
return worldImageSigFinish(res);
|
||||
}
|
||||
async function worldImageSigFinish(args) {
|
||||
// API.$on('WORLDIMAGE:SIGFINISH')
|
||||
const { fileId, fileVersion } = args.params;
|
||||
const parmas = {
|
||||
id: worldImage.value.worldId,
|
||||
imageUrl: `${API.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
||||
imageUrl: `${AppGlobal.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
||||
};
|
||||
const res = await imageRequest.setWorldImage(parmas);
|
||||
return worldImageSet(res);
|
||||
@@ -337,7 +321,7 @@
|
||||
});
|
||||
refresh();
|
||||
} else {
|
||||
API.$throw(0, 'World image change failed', args.params.imageUrl);
|
||||
$throw(0, 'World image change failed', args.params.imageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,8 +330,8 @@
|
||||
function setWorldImage(image) {
|
||||
changeWorldImageDialogLoading.value = true;
|
||||
const parmas = {
|
||||
id: props.worldDialog.id,
|
||||
imageUrl: `${API.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
||||
id: worldDialog.value.id,
|
||||
imageUrl: `${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
||||
};
|
||||
imageRequest
|
||||
.setWorldImage(parmas)
|
||||
@@ -360,35 +344,11 @@
|
||||
|
||||
function compareCurrentImage(image) {
|
||||
if (
|
||||
`${API.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
||||
// FIXME: old:avatarDialog -> new:worldDialog, is this correct?
|
||||
props.worldDialog.ref.imageUrl
|
||||
`${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
||||
worldDialog.value.ref.imageUrl
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// $app.methods.deleteWorldImage = function () {
|
||||
// this.changeWorldImageDialogLoading = true;
|
||||
// var parmas = {
|
||||
// fileId: this.previousImagesTableFileId,
|
||||
// version: this.previousImagesTable[0].version
|
||||
// };
|
||||
// vrcPlusIconRequest
|
||||
// .deleteFileVersion(parmas)
|
||||
// .then((args) => {
|
||||
// this.previousImagesTableFileId = args.json.id;
|
||||
// var images = [];
|
||||
// args.json.versions.forEach((item) => {
|
||||
// if (!item.deleted) {
|
||||
// images.unshift(item);
|
||||
// }
|
||||
// });
|
||||
// this.checkPreviousImageAvailable(images);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// this.changeWorldImageDialogLoading = false;
|
||||
// });
|
||||
// };
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
:visible.sync="isVisible"
|
||||
:title="$t('dialog.set_world_tags.header')"
|
||||
:title="t('dialog.set_world_tags.header')"
|
||||
width="400px"
|
||||
destroy-on-close
|
||||
append-to-body>
|
||||
<el-checkbox v-model="setWorldTagsDialog.avatarScalingDisabled">
|
||||
{{ $t('dialog.set_world_tags.avatar_scaling_disabled') }}
|
||||
{{ t('dialog.set_world_tags.avatar_scaling_disabled') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.focusViewDisabled">
|
||||
{{ $t('dialog.set_world_tags.focus_view_disabled') }}
|
||||
{{ t('dialog.set_world_tags.focus_view_disabled') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.debugAllowed">
|
||||
{{ $t('dialog.set_world_tags.enable_debugging') }}
|
||||
{{ t('dialog.set_world_tags.enable_debugging') }}
|
||||
</el-checkbox>
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ $t('dialog.set_world_tags.author_tags') }}<br /></div>
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ t('dialog.set_world_tags.author_tags') }}<br /></div>
|
||||
<el-input
|
||||
v-model="setWorldTagsDialog.authorTags"
|
||||
type="textarea"
|
||||
@@ -25,281 +25,286 @@
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
placeholder=""
|
||||
style="margin-top: 10px"></el-input>
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ $t('dialog.set_world_tags.content_tags') }}<br /></div>
|
||||
<div style="font-size: 12px; margin-top: 10px">{{ t('dialog.set_world_tags.content_tags') }}<br /></div>
|
||||
<el-checkbox v-model="setWorldTagsDialog.contentHorror">
|
||||
{{ $t('dialog.set_world_tags.content_horror') }}
|
||||
{{ t('dialog.set_world_tags.content_horror') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.contentGore">
|
||||
{{ $t('dialog.set_world_tags.content_gore') }}
|
||||
{{ t('dialog.set_world_tags.content_gore') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.contentViolence">
|
||||
{{ $t('dialog.set_world_tags.content_violence') }}
|
||||
{{ t('dialog.set_world_tags.content_violence') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.contentAdult">
|
||||
{{ $t('dialog.set_world_tags.content_adult') }}
|
||||
{{ t('dialog.set_world_tags.content_adult') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.contentSex">
|
||||
{{ $t('dialog.set_world_tags.content_sex') }}
|
||||
{{ t('dialog.set_world_tags.content_sex') }}
|
||||
</el-checkbox>
|
||||
<div style="font-size: 12px; margin-top: 10px">
|
||||
{{ $t('dialog.set_world_tags.default_content_settings') }}<br />
|
||||
{{ t('dialog.set_world_tags.default_content_settings') }}<br />
|
||||
</div>
|
||||
<el-checkbox v-model="setWorldTagsDialog.emoji">
|
||||
{{ $t('dialog.new_instance.content_emoji') }}
|
||||
{{ t('dialog.new_instance.content_emoji') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.stickers">
|
||||
{{ $t('dialog.new_instance.content_stickers') }}
|
||||
{{ t('dialog.new_instance.content_stickers') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.pedestals">
|
||||
{{ $t('dialog.new_instance.content_pedestals') }}
|
||||
{{ t('dialog.new_instance.content_pedestals') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.prints">
|
||||
{{ $t('dialog.new_instance.content_prints') }}
|
||||
{{ t('dialog.new_instance.content_prints') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.drones">
|
||||
{{ $t('dialog.new_instance.content_drones') }}
|
||||
{{ t('dialog.new_instance.content_drones') }}
|
||||
</el-checkbox>
|
||||
<br />
|
||||
<el-checkbox v-model="setWorldTagsDialog.props">
|
||||
{{ $t('dialog.new_instance.content_items') }}
|
||||
{{ t('dialog.new_instance.content_items') }}
|
||||
</el-checkbox>
|
||||
<template #footer>
|
||||
<div style="display: flex">
|
||||
<el-button size="small" @click="setWorldTagsDialog.visible = false">
|
||||
{{ $t('dialog.set_world_tags.cancel') }}
|
||||
{{ t('dialog.set_world_tags.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" @click="saveSetWorldTagsDialog">
|
||||
{{ $t('dialog.set_world_tags.save') }}
|
||||
{{ t('dialog.set_world_tags.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, watch, getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { worldRequest } from '../../../api';
|
||||
import { useWorldStore } from '../../../stores';
|
||||
|
||||
export default {
|
||||
name: 'SetWorldTagsDialog',
|
||||
inject: ['showWorldDialog'],
|
||||
props: {
|
||||
oldTags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
isSetWorldTagsDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
worldId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isWorldDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
oldTags: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
setWorldTagsDialog: {
|
||||
authorTags: [],
|
||||
contentTags: [],
|
||||
debugAllowed: false,
|
||||
avatarScalingDisabled: false,
|
||||
focusViewDisabled: false,
|
||||
contentHorror: false,
|
||||
contentGore: false,
|
||||
contentViolence: false,
|
||||
contentAdult: false,
|
||||
contentSex: false,
|
||||
emoji: true,
|
||||
stickers: true,
|
||||
pedestals: true,
|
||||
prints: true,
|
||||
drones: true,
|
||||
props: true
|
||||
}
|
||||
};
|
||||
isSetWorldTagsDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.isSetWorldTagsDialogVisible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:is-set-world-tags-dialog-visible', val);
|
||||
}
|
||||
}
|
||||
worldId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
watch: {
|
||||
isSetWorldTagsDialogVisible(val) {
|
||||
if (val) {
|
||||
this.showSetWorldTagsDialog();
|
||||
}
|
||||
}
|
||||
isWorldDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:isSetWorldTagsDialogVisible']);
|
||||
|
||||
const { showWorldDialog } = useWorldStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const setWorldTagsDialog = ref({
|
||||
authorTags: '',
|
||||
contentTags: '',
|
||||
debugAllowed: false,
|
||||
avatarScalingDisabled: false,
|
||||
focusViewDisabled: false,
|
||||
contentHorror: false,
|
||||
contentGore: false,
|
||||
contentViolence: false,
|
||||
contentAdult: false,
|
||||
contentSex: false,
|
||||
emoji: true,
|
||||
stickers: true,
|
||||
pedestals: true,
|
||||
prints: true,
|
||||
drones: true,
|
||||
props: true
|
||||
});
|
||||
|
||||
const isVisible = computed({
|
||||
get() {
|
||||
return props.isSetWorldTagsDialogVisible;
|
||||
},
|
||||
methods: {
|
||||
showSetWorldTagsDialog() {
|
||||
const D = this.setWorldTagsDialog;
|
||||
D.visible = true;
|
||||
D.debugAllowed = false;
|
||||
D.avatarScalingDisabled = false;
|
||||
D.focusViewDisabled = false;
|
||||
D.contentHorror = false;
|
||||
D.contentGore = false;
|
||||
D.contentViolence = false;
|
||||
D.contentAdult = false;
|
||||
D.contentSex = false;
|
||||
const authorTags = [];
|
||||
const contentTags = [];
|
||||
this.oldTags.forEach((tag) => {
|
||||
if (tag.startsWith('author_tag_')) {
|
||||
authorTags.unshift(tag.substring(11));
|
||||
}
|
||||
if (tag.startsWith('content_')) {
|
||||
contentTags.unshift(tag.substring(8));
|
||||
}
|
||||
switch (tag) {
|
||||
case 'content_horror':
|
||||
D.contentHorror = true;
|
||||
break;
|
||||
case 'content_gore':
|
||||
D.contentGore = true;
|
||||
break;
|
||||
case 'content_violence':
|
||||
D.contentViolence = true;
|
||||
break;
|
||||
case 'content_adult':
|
||||
D.contentAdult = true;
|
||||
break;
|
||||
case 'content_sex':
|
||||
D.contentSex = true;
|
||||
break;
|
||||
case 'debug_allowed':
|
||||
D.debugAllowed = true;
|
||||
break;
|
||||
case 'feature_avatar_scaling_disabled':
|
||||
D.avatarScalingDisabled = true;
|
||||
break;
|
||||
case 'feature_focus_view_disabled':
|
||||
D.focusViewDisabled = true;
|
||||
break;
|
||||
case 'feature_emoji_disabled':
|
||||
D.emoji = false;
|
||||
break;
|
||||
case 'feature_stickers_disabled':
|
||||
D.stickers = false;
|
||||
break;
|
||||
case 'feature_pedestals_disabled':
|
||||
D.pedestals = false;
|
||||
break;
|
||||
case 'feature_prints_disabled':
|
||||
D.prints = false;
|
||||
break;
|
||||
case 'feature_drones_disabled':
|
||||
D.drones = false;
|
||||
break;
|
||||
case 'feature_props_disabled':
|
||||
D.props = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
D.authorTags = authorTags.toString();
|
||||
D.contentTags = contentTags.toString();
|
||||
},
|
||||
saveSetWorldTagsDialog() {
|
||||
const D = this.setWorldTagsDialog;
|
||||
const authorTags = D.authorTags.trim().split(',');
|
||||
const contentTags = D.contentTags.trim().split(',');
|
||||
const tags = [];
|
||||
authorTags.forEach((tag) => {
|
||||
if (tag) {
|
||||
tags.unshift(`author_tag_${tag}`);
|
||||
}
|
||||
});
|
||||
// add back custom tags
|
||||
contentTags.forEach((tag) => {
|
||||
switch (tag) {
|
||||
case 'horror':
|
||||
case 'gore':
|
||||
case 'violence':
|
||||
case 'adult':
|
||||
case 'sex':
|
||||
case '':
|
||||
break;
|
||||
default:
|
||||
tags.unshift(`content_${tag}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (D.contentHorror) {
|
||||
tags.unshift('content_horror');
|
||||
}
|
||||
if (D.contentGore) {
|
||||
tags.unshift('content_gore');
|
||||
}
|
||||
if (D.contentViolence) {
|
||||
tags.unshift('content_violence');
|
||||
}
|
||||
if (D.contentAdult) {
|
||||
tags.unshift('content_adult');
|
||||
}
|
||||
if (D.contentSex) {
|
||||
tags.unshift('content_sex');
|
||||
}
|
||||
if (D.debugAllowed) {
|
||||
tags.unshift('debug_allowed');
|
||||
}
|
||||
if (D.avatarScalingDisabled) {
|
||||
tags.unshift('feature_avatar_scaling_disabled');
|
||||
}
|
||||
if (D.focusViewDisabled) {
|
||||
tags.unshift('feature_focus_view_disabled');
|
||||
}
|
||||
if (!D.emoji) {
|
||||
tags.unshift('feature_emoji_disabled');
|
||||
}
|
||||
if (!D.stickers) {
|
||||
tags.unshift('feature_stickers_disabled');
|
||||
}
|
||||
if (!D.pedestals) {
|
||||
tags.unshift('feature_pedestals_disabled');
|
||||
}
|
||||
if (!D.prints) {
|
||||
tags.unshift('feature_prints_disabled');
|
||||
}
|
||||
if (!D.drones) {
|
||||
tags.unshift('feature_drones_disabled');
|
||||
}
|
||||
if (!D.props) {
|
||||
tags.unshift('feature_props_disabled');
|
||||
}
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: this.worldId,
|
||||
tags
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: 'Tags updated',
|
||||
type: 'success'
|
||||
});
|
||||
this.$emit('update:is-set-world-tags-dialog-visible', false);
|
||||
if (this.isWorldDialogVisible) {
|
||||
this.showWorldDialog(args.json.id);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
set(val) {
|
||||
emit('update:isSetWorldTagsDialogVisible', val);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.isSetWorldTagsDialogVisible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
showSetWorldTagsDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function showSetWorldTagsDialog() {
|
||||
const D = setWorldTagsDialog.value;
|
||||
|
||||
D.debugAllowed = false;
|
||||
D.avatarScalingDisabled = false;
|
||||
D.focusViewDisabled = false;
|
||||
D.contentHorror = false;
|
||||
D.contentGore = false;
|
||||
D.contentViolence = false;
|
||||
D.contentAdult = false;
|
||||
D.contentSex = false;
|
||||
const authorTags = [];
|
||||
const contentTags = [];
|
||||
props.oldTags.forEach((tag) => {
|
||||
if (tag.startsWith('author_tag_')) {
|
||||
authorTags.unshift(tag.substring(11));
|
||||
}
|
||||
if (tag.startsWith('content_')) {
|
||||
contentTags.unshift(tag.substring(8));
|
||||
}
|
||||
switch (tag) {
|
||||
case 'content_horror':
|
||||
D.contentHorror = true;
|
||||
break;
|
||||
case 'content_gore':
|
||||
D.contentGore = true;
|
||||
break;
|
||||
case 'content_violence':
|
||||
D.contentViolence = true;
|
||||
break;
|
||||
case 'content_adult':
|
||||
D.contentAdult = true;
|
||||
break;
|
||||
case 'content_sex':
|
||||
D.contentSex = true;
|
||||
break;
|
||||
case 'debug_allowed':
|
||||
D.debugAllowed = true;
|
||||
break;
|
||||
case 'feature_avatar_scaling_disabled':
|
||||
D.avatarScalingDisabled = true;
|
||||
break;
|
||||
case 'feature_focus_view_disabled':
|
||||
D.focusViewDisabled = true;
|
||||
break;
|
||||
case 'feature_emoji_disabled':
|
||||
D.emoji = false;
|
||||
break;
|
||||
case 'feature_stickers_disabled':
|
||||
D.stickers = false;
|
||||
break;
|
||||
case 'feature_pedestals_disabled':
|
||||
D.pedestals = false;
|
||||
break;
|
||||
case 'feature_prints_disabled':
|
||||
D.prints = false;
|
||||
break;
|
||||
case 'feature_drones_disabled':
|
||||
D.drones = false;
|
||||
break;
|
||||
case 'feature_props_disabled':
|
||||
D.props = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
D.authorTags = authorTags.toString();
|
||||
D.contentTags = contentTags.toString();
|
||||
}
|
||||
|
||||
function saveSetWorldTagsDialog() {
|
||||
const D = setWorldTagsDialog.value;
|
||||
const authorTags = D.authorTags.trim().split(',');
|
||||
const contentTags = D.contentTags.trim().split(',');
|
||||
const tags = [];
|
||||
authorTags.forEach((tag) => {
|
||||
if (tag) {
|
||||
tags.unshift(`author_tag_${tag}`);
|
||||
}
|
||||
});
|
||||
// add back custom tags
|
||||
contentTags.forEach((tag) => {
|
||||
switch (tag) {
|
||||
case 'horror':
|
||||
case 'gore':
|
||||
case 'violence':
|
||||
case 'adult':
|
||||
case 'sex':
|
||||
case '':
|
||||
break;
|
||||
default:
|
||||
tags.unshift(`content_${tag}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (D.contentHorror) {
|
||||
tags.unshift('content_horror');
|
||||
}
|
||||
if (D.contentGore) {
|
||||
tags.unshift('content_gore');
|
||||
}
|
||||
if (D.contentViolence) {
|
||||
tags.unshift('content_violence');
|
||||
}
|
||||
if (D.contentAdult) {
|
||||
tags.unshift('content_adult');
|
||||
}
|
||||
if (D.contentSex) {
|
||||
tags.unshift('content_sex');
|
||||
}
|
||||
if (D.debugAllowed) {
|
||||
tags.unshift('debug_allowed');
|
||||
}
|
||||
if (D.avatarScalingDisabled) {
|
||||
tags.unshift('feature_avatar_scaling_disabled');
|
||||
}
|
||||
if (D.focusViewDisabled) {
|
||||
tags.unshift('feature_focus_view_disabled');
|
||||
}
|
||||
if (!D.emoji) {
|
||||
tags.unshift('feature_emoji_disabled');
|
||||
}
|
||||
if (!D.stickers) {
|
||||
tags.unshift('feature_stickers_disabled');
|
||||
}
|
||||
if (!D.pedestals) {
|
||||
tags.unshift('feature_pedestals_disabled');
|
||||
}
|
||||
if (!D.prints) {
|
||||
tags.unshift('feature_prints_disabled');
|
||||
}
|
||||
if (!D.drones) {
|
||||
tags.unshift('feature_drones_disabled');
|
||||
}
|
||||
if (!D.props) {
|
||||
tags.unshift('feature_props_disabled');
|
||||
}
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: props.worldId,
|
||||
tags
|
||||
})
|
||||
.then((args) => {
|
||||
proxy.$message({
|
||||
message: 'Tags updated',
|
||||
type: 'success'
|
||||
});
|
||||
emit('update:isSetWorldTagsDialogVisible', false);
|
||||
if (props.isWorldDialogVisible) {
|
||||
showWorldDialog(args.json.id);
|
||||
}
|
||||
return args;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<safe-dialog
|
||||
:visible.sync="isVisible"
|
||||
:title="$t('dialog.allowed_video_player_domains.header')"
|
||||
:title="t('dialog.allowed_video_player_domains.header')"
|
||||
width="600px"
|
||||
destroy-on-close
|
||||
append-to-body>
|
||||
@@ -10,13 +10,12 @@
|
||||
v-for="(domain, index) in urlList"
|
||||
:key="index"
|
||||
v-model="urlList[index]"
|
||||
:value="domain"
|
||||
size="small"
|
||||
style="margin-top: 5px">
|
||||
<el-button slot="append" icon="el-icon-delete" @click="urlList.splice(index, 1)"></el-button>
|
||||
</el-input>
|
||||
<el-button size="mini" style="margin-top: 5px" @click="urlList.push('')">
|
||||
{{ $t('dialog.allowed_video_player_domains.add_domain') }}
|
||||
{{ t('dialog.allowed_video_player_domains.add_domain') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -25,65 +24,67 @@
|
||||
size="small"
|
||||
:disabled="!worldAllowedDomainsDialog.worldId"
|
||||
@click="saveWorldAllowedDomains">
|
||||
{{ $t('dialog.allowed_video_player_domains.save') }}
|
||||
{{ t('dialog.allowed_video_player_domains.save') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</safe-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, watch, getCurrentInstance } from 'vue';
|
||||
import { useI18n } from 'vue-i18n-bridge';
|
||||
import { worldRequest } from '../../../api';
|
||||
|
||||
export default {
|
||||
name: 'WorldAllowedDomainsDialog',
|
||||
props: {
|
||||
worldAllowedDomainsDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
worldAllowedDomainsDialog: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:worldAllowedDomainsDialog']);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const urlList = ref([]);
|
||||
|
||||
const isVisible = computed({
|
||||
get() {
|
||||
return props.worldAllowedDomainsDialog.visible;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
urlList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVisible: {
|
||||
get() {
|
||||
return this.worldAllowedDomainsDialog.visible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:world-allowed-domains-dialog', {
|
||||
...this.worldAllowedDomainsDialog,
|
||||
visible: val
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'worldAllowedDomainsDialog.visible'(val) {
|
||||
if (val) {
|
||||
this.urlList = this.worldAllowedDomainsDialog.urlList;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveWorldAllowedDomains() {
|
||||
const D = this.worldAllowedDomainsDialog;
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: D.worldId,
|
||||
urlList: D.urlList
|
||||
})
|
||||
.then((args) => {
|
||||
this.$message({
|
||||
message: 'Allowed Video Player Domains updated',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
set(val) {
|
||||
emit('update:worldAllowedDomainsDialog', {
|
||||
...props.worldAllowedDomainsDialog,
|
||||
visible: val
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.worldAllowedDomainsDialog.visible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
urlList.value = props.worldAllowedDomainsDialog.urlList;
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
function saveWorldAllowedDomains() {
|
||||
const D = props.worldAllowedDomainsDialog;
|
||||
worldRequest
|
||||
.saveWorld({
|
||||
id: D.worldId,
|
||||
urlList: urlList.value
|
||||
})
|
||||
.then((args) => {
|
||||
proxy.$message({
|
||||
message: 'Allowed Video Player Domains updated',
|
||||
type: 'success'
|
||||
});
|
||||
return args;
|
||||
});
|
||||
D.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import configRepository from '../../service/config';
|
||||
|
||||
function useModerationTable() {
|
||||
const groupInvitesModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupJoinRequestsModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupBlockedModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupLogsModerationTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: ['description'], value: '' }],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupBansModerationTable = reactive({
|
||||
data: [],
|
||||
filters: [{ prop: ['$displayName'], value: '' }],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
const groupMemberModerationTable = reactive({
|
||||
data: [],
|
||||
tableProps: { stripe: true, size: 'mini' },
|
||||
pageSize: 15,
|
||||
paginationProps: {
|
||||
small: true,
|
||||
layout: 'sizes,prev,pager,next,total',
|
||||
pageSizes: [10, 15, 20, 25, 50, 100]
|
||||
}
|
||||
});
|
||||
|
||||
async function initializePageSize() {
|
||||
try {
|
||||
const tablePageSize = await configRepository.getInt(
|
||||
'VRCX_tablePageSize',
|
||||
15
|
||||
);
|
||||
groupMemberModerationTable.pageSize = tablePageSize;
|
||||
groupBansModerationTable.pageSize = tablePageSize;
|
||||
groupLogsModerationTable.pageSize = tablePageSize;
|
||||
groupInvitesModerationTable.pageSize = tablePageSize;
|
||||
groupJoinRequestsModerationTable.pageSize = tablePageSize;
|
||||
groupBlockedModerationTable.pageSize = tablePageSize;
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize table page size:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function deselectGroupMember(userId) {
|
||||
const deselectInTable = (tableData) => {
|
||||
if (userId) {
|
||||
const row = tableData.find((item) => item.userId === userId);
|
||||
if (row) {
|
||||
row.$selected = false;
|
||||
}
|
||||
} else {
|
||||
tableData.forEach((row) => {
|
||||
if (row.$selected) {
|
||||
row.$selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
deselectInTable(groupMemberModerationTable.data);
|
||||
deselectInTable(groupBansModerationTable.data);
|
||||
deselectInTable(groupInvitesModerationTable.data);
|
||||
deselectInTable(groupJoinRequestsModerationTable.data);
|
||||
deselectInTable(groupBlockedModerationTable.data);
|
||||
}
|
||||
|
||||
return {
|
||||
groupInvitesModerationTable,
|
||||
groupJoinRequestsModerationTable,
|
||||
groupBlockedModerationTable,
|
||||
groupLogsModerationTable,
|
||||
groupBansModerationTable,
|
||||
groupMemberModerationTable,
|
||||
initializePageSize,
|
||||
deselectGroupMember
|
||||
};
|
||||
}
|
||||
|
||||
function useSelectedUsers() {
|
||||
const selectedUsers = reactive({});
|
||||
// computed not working here hmm
|
||||
const selectedUsersArray = ref([]);
|
||||
|
||||
function groupMemberModerationTableSelectionChange(row) {
|
||||
if (row.$selected && !selectedUsers[row.userId]) {
|
||||
setSelectedUsers(row.userId, row);
|
||||
} else if (!row.$selected && selectedUsers[row.userId]) {
|
||||
deselectedUsers(row.userId);
|
||||
}
|
||||
}
|
||||
|
||||
function deselectedUsers(userId, isAll = false) {
|
||||
if (isAll) {
|
||||
for (const id in selectedUsers) {
|
||||
if (Object.prototype.hasOwnProperty.call(selectedUsers, id)) {
|
||||
delete selectedUsers[id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Object.prototype.hasOwnProperty.call(selectedUsers, userId)) {
|
||||
delete selectedUsers[userId];
|
||||
}
|
||||
}
|
||||
selectedUsersArray.value = Object.values(selectedUsers);
|
||||
}
|
||||
|
||||
function setSelectedUsers(usersId, user) {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
selectedUsers[usersId] = user;
|
||||
selectedUsersArray.value = Object.values(selectedUsers);
|
||||
}
|
||||
return {
|
||||
selectedUsers,
|
||||
selectedUsersArray,
|
||||
groupMemberModerationTableSelectionChange,
|
||||
deselectedUsers,
|
||||
setSelectedUsers
|
||||
};
|
||||
}
|
||||
|
||||
export { useModerationTable, useSelectedUsers };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user