mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 06:56:04 +02:00
Avatar gallery and listing
This commit is contained in:
@@ -167,6 +167,54 @@ const avatarReq = {
|
|||||||
// window.API.$emit('AVATAR:STYLES', args);
|
// window.API.$emit('AVATAR:STYLES', args);
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ avatarId: string }} params
|
||||||
|
* @returns {Promise<{json: any, params}>}
|
||||||
|
*/
|
||||||
|
getAvatarGallery(avatarId) {
|
||||||
|
const params = {
|
||||||
|
tag: 'avatargallery',
|
||||||
|
galleryId: avatarId,
|
||||||
|
n: 100,
|
||||||
|
offset: 0
|
||||||
|
};
|
||||||
|
return window.API.call(`files`, {
|
||||||
|
params,
|
||||||
|
method: 'GET'
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
// window.API.$emit('AVATAR:GALLERY', args);
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ imageData: string, avatarId: string }} params
|
||||||
|
* @returns {Promise<{json: any, params}>}
|
||||||
|
*/
|
||||||
|
uploadAvatarGalleryImage(imageData, avatarId) {
|
||||||
|
const params = {
|
||||||
|
tag: 'avatargallery',
|
||||||
|
galleryId: avatarId
|
||||||
|
};
|
||||||
|
return window.API.call('file/image', {
|
||||||
|
uploadImage: true,
|
||||||
|
matchingDimensions: false,
|
||||||
|
postData: JSON.stringify(params),
|
||||||
|
imageData
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
// window.API.$emit('AVATARGALLERYIMAGE:ADD', args);
|
||||||
|
return args;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|||||||
@@ -185,6 +185,18 @@ const miscReq = {
|
|||||||
// window.API.$emit('VISITS', args);
|
// window.API.$emit('VISITS', args);
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFile(fileId) {
|
||||||
|
return window.API.call(`file/${fileId}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
fileId
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
|
|||||||
@@ -15,18 +15,6 @@ const VRCPlusIconsReq = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteFile(fileId) {
|
|
||||||
return window.API.call(`file/${fileId}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
fileId
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadVRCPlusIcon(imageData) {
|
uploadVRCPlusIcon(imageData) {
|
||||||
const params = {
|
const params = {
|
||||||
tag: 'icon'
|
tag: 'icon'
|
||||||
|
|||||||
+30
-1
@@ -356,7 +356,9 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
languageClass: this.languageClass,
|
languageClass: this.languageClass,
|
||||||
showGroupDialog: this.showGroupDialog,
|
showGroupDialog: this.showGroupDialog,
|
||||||
showGallerySelectDialog: this.showGallerySelectDialog,
|
showGallerySelectDialog: this.showGallerySelectDialog,
|
||||||
showGalleryDialog: this.showGalleryDialog
|
showGalleryDialog: this.showGalleryDialog,
|
||||||
|
getImageUrlFromImageId: this.getImageUrlFromImageId,
|
||||||
|
getAvatarGallery: this.getAvatarGallery
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
el: '#root',
|
el: '#root',
|
||||||
@@ -1474,6 +1476,10 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
ref.unityPackages = unityPackages;
|
ref.unityPackages = unityPackages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const listing of ref?.publishedListings) {
|
||||||
|
listing.displayName = $utils.replaceBioSymbols(listing.displayName);
|
||||||
|
listing.description = $utils.replaceBioSymbols(listing.description);
|
||||||
|
}
|
||||||
ref.name = $utils.replaceBioSymbols(ref.name);
|
ref.name = $utils.replaceBioSymbols(ref.name);
|
||||||
ref.description = $utils.replaceBioSymbols(ref.description);
|
ref.description = $utils.replaceBioSymbols(ref.description);
|
||||||
return ref;
|
return ref;
|
||||||
@@ -9528,6 +9534,7 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
isIos: false,
|
isIos: false,
|
||||||
bundleSizes: [],
|
bundleSizes: [],
|
||||||
platformInfo: {},
|
platformInfo: {},
|
||||||
|
galleryImages: [],
|
||||||
lastUpdated: '',
|
lastUpdated: '',
|
||||||
inCache: false,
|
inCache: false,
|
||||||
cacheSize: 0,
|
cacheSize: 0,
|
||||||
@@ -9570,6 +9577,7 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
D.lastUpdated = '';
|
D.lastUpdated = '';
|
||||||
D.bundleSizes = [];
|
D.bundleSizes = [];
|
||||||
D.platformInfo = {};
|
D.platformInfo = {};
|
||||||
|
D.galleryImages = [];
|
||||||
D.isFavorite =
|
D.isFavorite =
|
||||||
API.cachedFavoritesByObjectId.has(avatarId) ||
|
API.cachedFavoritesByObjectId.has(avatarId) ||
|
||||||
(API.currentUser.$isVRCPlus &&
|
(API.currentUser.$isVRCPlus &&
|
||||||
@@ -9592,6 +9600,7 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
.then((args) => {
|
.then((args) => {
|
||||||
var { ref } = args;
|
var { ref } = args;
|
||||||
D.ref = ref;
|
D.ref = ref;
|
||||||
|
this.getAvatarGallery(avatarId);
|
||||||
this.updateVRChatAvatarCache();
|
this.updateVRChatAvatarCache();
|
||||||
if (/quest/.test(ref.tags)) {
|
if (/quest/.test(ref.tags)) {
|
||||||
D.isQuestFallback = true;
|
D.isQuestFallback = true;
|
||||||
@@ -9626,6 +9635,22 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$app.methods.getAvatarGallery = async function (avatarId) {
|
||||||
|
var D = this.avatarDialog;
|
||||||
|
const args = await avatarRequest.getAvatarGallery(avatarId);
|
||||||
|
if (args.params.galleryId !== D.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
D.galleryImages = [];
|
||||||
|
for (const file of args.json) {
|
||||||
|
const url = file.versions[file.versions.length - 1].file.url;
|
||||||
|
D.galleryImages.push(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for JSON tab treeData
|
||||||
|
D.ref.gallery = args.json;
|
||||||
|
};
|
||||||
|
|
||||||
$app.methods.selectAvatarWithConfirmation = function (id) {
|
$app.methods.selectAvatarWithConfirmation = function (id) {
|
||||||
this.$confirm(`Continue? Select Avatar`, 'Confirm', {
|
this.$confirm(`Continue? Select Avatar`, 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
@@ -11153,6 +11178,10 @@ console.log(`isLinux: ${LINUX}`);
|
|||||||
return user.currentAvatarImageUrl;
|
return user.currentAvatarImageUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$app.methods.getImageUrlFromImageId = function (imageId) {
|
||||||
|
return `https://api.vrchat.cloud/api/1/file/${imageId}/1/`;
|
||||||
|
};
|
||||||
|
|
||||||
$app.methods.showConsole = function () {
|
$app.methods.showConsole = function () {
|
||||||
AppApi.ShowDevTools();
|
AppApi.ShowDevTools();
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -940,6 +940,11 @@ i.x-status-icon.red {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-carousel__mask {
|
||||||
|
// looks bad
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
// Something changed the CSS of element-ui
|
// Something changed the CSS of element-ui
|
||||||
// The other parts are the same
|
// The other parts are the same
|
||||||
|
|||||||
@@ -555,8 +555,13 @@ export default class extends baseClass {
|
|||||||
// hmm
|
// hmm
|
||||||
} else if (contentType === 'created') {
|
} else if (contentType === 'created') {
|
||||||
// on avatar upload
|
// on avatar upload
|
||||||
|
} else if (contentType === 'avatargallery') {
|
||||||
|
// on avatar gallery image upload
|
||||||
} else {
|
} else {
|
||||||
console.log('Unknown content-refresh', content);
|
console.log(
|
||||||
|
'Unknown content-refresh type',
|
||||||
|
content.contentType
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -365,7 +365,70 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-tabs>
|
<el-tabs>
|
||||||
<el-tab-pane :label="t('dialog.avatar.info.header')">
|
<el-tab-pane :label="t('dialog.avatar.info.header')">
|
||||||
<div class="x-friend-list">
|
<div class="x-friend-list" style="max-height: unset">
|
||||||
|
<div
|
||||||
|
v-if="avatarDialog.galleryImages.length || avatarDialog.ref.authorId === API.currentUser.id"
|
||||||
|
style="width: 100%">
|
||||||
|
<span class="name">{{ t('dialog.avatar.info.gallery') }}</span>
|
||||||
|
<input
|
||||||
|
id="AvatarGalleryUploadButton"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
style="display: none"
|
||||||
|
@change="onFileChangeAvatarGallery" />
|
||||||
|
<el-button
|
||||||
|
v-if="avatarDialog.ref.authorId === API.currentUser.id"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-upload2"
|
||||||
|
style="margin-left: 5px"
|
||||||
|
@click="displayAvatarGalleryUpload"
|
||||||
|
>{{ t('dialog.screenshot_metadata.upload') }}</el-button
|
||||||
|
>
|
||||||
|
<el-carousel
|
||||||
|
v-if="avatarDialog.galleryImages.length"
|
||||||
|
type="card"
|
||||||
|
:autoplay="false"
|
||||||
|
height="200px">
|
||||||
|
<el-carousel-item v-for="imageUrl in avatarDialog.galleryImages" :key="imageUrl">
|
||||||
|
<img
|
||||||
|
:src="imageUrl"
|
||||||
|
style="width: 100%; height: 100%; object-fit: contain"
|
||||||
|
@click="showFullscreenImageDialog(imageUrl)" />
|
||||||
|
<el-button
|
||||||
|
v-if="avatarDialog.ref.authorId === API.currentUser.id"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
circle
|
||||||
|
class="x-link"
|
||||||
|
style="position: absolute; bottom: 5px; right: 5px"
|
||||||
|
@click.stop="deleteAvatarGalleryImage(imageUrl)"></el-button>
|
||||||
|
</el-carousel-item>
|
||||||
|
</el-carousel>
|
||||||
|
</div>
|
||||||
|
<div v-if="avatarDialog.ref.publishedListings?.length">
|
||||||
|
<span class="name">{{ t('dialog.avatar.info.listings') }}</span>
|
||||||
|
<div
|
||||||
|
v-for="listing in avatarDialog.ref.publishedListings"
|
||||||
|
:key="listing.id"
|
||||||
|
class="x-friend-item"
|
||||||
|
style="width: 100%; cursor: default">
|
||||||
|
<div class="avatar">
|
||||||
|
<img
|
||||||
|
:src="getImageUrlFromImageId(listing.imageId)"
|
||||||
|
@click="showFullscreenImageDialog(getImageUrlFromImageId(listing.imageId))" />
|
||||||
|
</div>
|
||||||
|
<div class="detail">
|
||||||
|
<span class="name">{{ listing.displayName }}</span>
|
||||||
|
<span class="extra" style="text-decoration: underline; font-style: italic"
|
||||||
|
>${{ commaNumber(listing.priceTokens) }}V</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="extra"
|
||||||
|
style="text-overflow: ellipsis; text-wrap: auto"
|
||||||
|
v-text="listing.description"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="x-friend-item" style="width: 100%; cursor: default">
|
<div class="x-friend-item" style="width: 100%; cursor: default">
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" style="margin-bottom: 5px">{{ t('dialog.avatar.info.memo') }}</span>
|
<span class="name" style="margin-bottom: 5px">{{ t('dialog.avatar.info.memo') }}</span>
|
||||||
@@ -551,6 +614,8 @@
|
|||||||
const showFavoriteDialog = inject('showFavoriteDialog');
|
const showFavoriteDialog = inject('showFavoriteDialog');
|
||||||
const openExternalLink = inject('openExternalLink');
|
const openExternalLink = inject('openExternalLink');
|
||||||
const adjustDialogZ = inject('adjustDialogZ');
|
const adjustDialogZ = inject('adjustDialogZ');
|
||||||
|
const getImageUrlFromImageId = inject('getImageUrlFromImageId');
|
||||||
|
const getAvatarGallery = inject('getAvatarGallery');
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
@@ -689,6 +754,10 @@
|
|||||||
emit('deleteVRChatCache', ref);
|
emit('deleteVRChatCache', ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function commaNumber(num) {
|
||||||
|
return utils.commaNumber(num);
|
||||||
|
}
|
||||||
|
|
||||||
function avatarDialogCommand(command) {
|
function avatarDialogCommand(command) {
|
||||||
const D = props.avatarDialog;
|
const D = props.avatarDialog;
|
||||||
switch (command) {
|
switch (command) {
|
||||||
@@ -1177,4 +1246,64 @@
|
|||||||
D.loading = false;
|
D.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function displayAvatarGalleryUpload() {
|
||||||
|
document.getElementById('AvatarGalleryUploadButton').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFileChangeAvatarGallery(e) {
|
||||||
|
const clearFile = function () {
|
||||||
|
if (document.querySelector('#AvatarGalleryUploadButton')) {
|
||||||
|
document.querySelector('#AvatarGalleryUploadButton').value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (files[0].size >= 100000000) {
|
||||||
|
// 100MB
|
||||||
|
$message({
|
||||||
|
message: t('message.file.too_large'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
clearFile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!files[0].type.match(/image.*/)) {
|
||||||
|
$message({
|
||||||
|
message: t('message.file.not_image'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
clearFile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const r = new FileReader();
|
||||||
|
r.onload = function () {
|
||||||
|
const base64Body = btoa(r.result);
|
||||||
|
avatarRequest.uploadAvatarGalleryImage(base64Body, props.avatarDialog.id).then((args) => {
|
||||||
|
$message({
|
||||||
|
message: t('message.avatar_gallery.uploaded'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
console.log(args);
|
||||||
|
getAvatarGallery(props.avatarDialog.id);
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
getAvatarGallery(props.avatarDialog.id);
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -447,7 +447,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance, inject, ref } from 'vue';
|
import { getCurrentInstance, inject, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n-bridge';
|
||||||
import { userRequest, vrcPlusIconRequest, vrcPlusImageRequest } from '../../../api';
|
import { userRequest, vrcPlusIconRequest, vrcPlusImageRequest, miscRequest } from '../../../api';
|
||||||
import { extractFileId } from '../../../composables/shared/utils';
|
import { extractFileId } from '../../../composables/shared/utils';
|
||||||
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../../composables/user/constants/emoji';
|
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../../composables/user/constants/emoji';
|
||||||
import { getPrintFileName } from '../../../composables/user/utils';
|
import { getPrintFileName } from '../../../composables/user/utils';
|
||||||
@@ -623,7 +623,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteGalleryImage(fileId) {
|
function deleteGalleryImage(fileId) {
|
||||||
vrcPlusIconRequest.deleteFile(fileId).then((args) => {
|
miscRequest.deleteFile(fileId).then((args) => {
|
||||||
// API.$emit('GALLERYIMAGE:DELETE', args);
|
// API.$emit('GALLERYIMAGE:DELETE', args);
|
||||||
// API.$on('GALLERYIMAGE:DELETE')
|
// API.$on('GALLERYIMAGE:DELETE')
|
||||||
const array = props.galleryTable;
|
const array = props.galleryTable;
|
||||||
@@ -726,7 +726,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteVRCPlusIcon(fileId) {
|
function deleteVRCPlusIcon(fileId) {
|
||||||
vrcPlusIconRequest.deleteFile(fileId).then((args) => {
|
miscRequest.deleteFile(fileId).then((args) => {
|
||||||
// API.$emit('VRCPLUSICON:DELETE', args);
|
// API.$emit('VRCPLUSICON:DELETE', args);
|
||||||
// API.$on('VRCPLUSICON:DELETE')
|
// API.$on('VRCPLUSICON:DELETE')
|
||||||
const array = props.VRCPlusIconsTable;
|
const array = props.VRCPlusIconsTable;
|
||||||
@@ -857,7 +857,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteEmoji(fileId) {
|
function deleteEmoji(fileId) {
|
||||||
vrcPlusIconRequest.deleteFile(fileId).then((args) => {
|
miscRequest.deleteFile(fileId).then((args) => {
|
||||||
// API.$emit('EMOJI:DELETE', args);
|
// API.$emit('EMOJI:DELETE', args);
|
||||||
// API.$on('EMOJI:DELETE')
|
// API.$on('EMOJI:DELETE')
|
||||||
const array = props.emojiTable;
|
const array = props.emojiTable;
|
||||||
@@ -927,7 +927,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteSticker(fileId) {
|
function deleteSticker(fileId) {
|
||||||
vrcPlusIconRequest.deleteFile(fileId).then((args) => {
|
miscRequest.deleteFile(fileId).then((args) => {
|
||||||
// API.$emit('STICKER:DELETE', args);
|
// API.$emit('STICKER:DELETE', args);
|
||||||
// API.$on('STICKER:DELETE')
|
// API.$on('STICKER:DELETE')
|
||||||
const array = props.stickerTable;
|
const array = props.stickerTable;
|
||||||
|
|||||||
@@ -981,7 +981,9 @@
|
|||||||
"platform": "Platform",
|
"platform": "Platform",
|
||||||
"time_spent": "Time Spent",
|
"time_spent": "Time Spent",
|
||||||
"memo": "Memo",
|
"memo": "Memo",
|
||||||
"memo_placeholder": "Click to add a memo"
|
"memo_placeholder": "Click to add a memo",
|
||||||
|
"listings": "Listings",
|
||||||
|
"gallery": "Gallery"
|
||||||
},
|
},
|
||||||
"json": {
|
"json": {
|
||||||
"header": "JSON",
|
"header": "JSON",
|
||||||
@@ -1635,6 +1637,11 @@
|
|||||||
"uploaded": "Gallery image uploaded",
|
"uploaded": "Gallery image uploaded",
|
||||||
"failed": "Failed to upload gallery image"
|
"failed": "Failed to upload gallery image"
|
||||||
},
|
},
|
||||||
|
"avatar_gallery": {
|
||||||
|
"uploaded": "Avatar gallery image uploaded",
|
||||||
|
"failed": "Failed to upload avatar gallery image",
|
||||||
|
"deleted": "Avatar gallery image deleted"
|
||||||
|
},
|
||||||
"world": {
|
"world": {
|
||||||
"image_changed": "World image changed",
|
"image_changed": "World image changed",
|
||||||
"image_invalid": "Current world image invalid"
|
"image_invalid": "Current world image invalid"
|
||||||
|
|||||||
Reference in New Issue
Block a user