Avatar dialog view/set styles

This commit is contained in:
Natsumi
2025-04-11 14:08:43 +10:00
parent 30b1bf3332
commit 59d3ead781
9 changed files with 220 additions and 12 deletions

View File

@@ -2672,7 +2672,7 @@ console.log(`isLinux: ${LINUX}`);
$app.data.vrcxId = '';
$app.methods.loadVrcxId = async function () {
vrcxId = await configRepository.getString(
this.vrcxId = await configRepository.getString(
'VRCX_id',
crypto.randomUUID()
);

View File

@@ -152,6 +152,21 @@ const avatarReq = {
// window.API.$emit('AVATAR:IMPOSTER:DELETE', args);
return args;
});
},
/**
* @returns {Promise<{json: any, params}>}
*/
getAvailableAvatarStyles() {
return window.API.call('avatarStyles', {
method: 'GET'
}).then((json) => {
const args = {
json
};
// window.API.$emit('AVATAR:STYLES', args);
return args;
});
}
};
// #endregion

View File

@@ -312,7 +312,7 @@
for (let j = 0; j < this.localAvatarFavorites[group].length; ++j) {
ref = this.localAvatarFavorites[group][j];
if (
typeof ref === 'undefined' ||
!ref ||
typeof ref.id === 'undefined' ||
typeof ref.name === 'undefined' ||
typeof ref.authorName === 'undefined'
@@ -330,7 +330,7 @@
for (let i = 0; i < this.favoriteAvatars.length; ++i) {
ref = this.favoriteAvatars[i].ref;
if (
typeof ref === 'undefined' ||
!ref ||
typeof ref.id === 'undefined' ||
typeof ref.name === 'undefined' ||
typeof ref.authorName === 'undefined'

View File

@@ -399,7 +399,7 @@
for (let j = 0; j < this.localWorldFavorites[group].length; ++j) {
ref = this.localWorldFavorites[group][j];
if (
typeof ref === 'undefined' ||
!ref ||
typeof ref.id === 'undefined' ||
typeof ref.name === 'undefined' ||
typeof ref.authorName === 'undefined'
@@ -417,7 +417,7 @@
for (let i = 0; i < this.favoriteWorlds.length; ++i) {
ref = this.favoriteWorlds[i].ref;
if (
typeof ref === 'undefined' ||
!ref ||
typeof ref.id === 'undefined' ||
typeof ref.name === 'undefined' ||
typeof ref.authorName === 'undefined'

View File

@@ -960,6 +960,7 @@
"rename": "Rename",
"change_description": "Change Description",
"change_content_tags": "Change Content Tags",
"change_styles": "Change Styles",
"change_image": "Change Image",
"download_package": "Download Unity Package",
"delete": "Delete",
@@ -1242,6 +1243,16 @@
"cancel": "Cancel",
"save": "Save"
},
"set_avatar_styles": {
"header": "Set Avatar Styles",
"primary_style": "Primary Style",
"secondary_style": "Secondary Style",
"select_style": "Select Style",
"save_success": "Avatar styles changed successfully",
"save_failed": "Failed to change avatar styles",
"cancel": "Cancel",
"save": "Save"
},
"vrcx_updater": {
"header": "VRCX Updater",
"latest_version": "VRCX is up to date.",

View File

@@ -83,7 +83,6 @@ mixin currentUser
ref='bioDialog'
:visible.sync='bioDialog.visible'
:title='$t("dialog.bio.header")'
:close-on-click-modal='false'
width='600px')
div(v-loading='bioDialog.loading')
el-input(

View File

@@ -125,6 +125,26 @@
<span v-text="avatarDialog.cacheSize"></span>
&nbsp;{{ t('dialog.avatar.tags.cache') }}
</el-tag>
<el-tag
v-if="avatarDialog.ref.styles?.primary || avatarDialog.ref.styles?.secondary"
type="info"
effect="plain"
size="mini"
style="margin-right: 5px; margin-top: 5px"
>Styles
<span
v-if="avatarDialog.ref.styles.primary"
class="x-grey"
style="margin-left: 5px; border-left: inherit; padding-left: 5px"
>{{ avatarDialog.ref.styles.primary }}</span
>
<span
v-if="avatarDialog.ref.styles.secondary"
class="x-grey"
style="margin-left: 5px; border-left: inherit; padding-left: 5px"
>{{ avatarDialog.ref.styles.secondary }}</span
>
</el-tag>
<el-tag
v-if="avatarDialog.isQuestFallback"
type="info"
@@ -297,6 +317,9 @@
<el-dropdown-item icon="el-icon-edit" command="Change Content Tags">{{
t('dialog.avatar.actions.change_content_tags')
}}</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" command="Change Styles">{{
t('dialog.avatar.actions.change_styles')
}}</el-dropdown-item>
<el-dropdown-item icon="el-icon-picture-outline" command="Change Image">{{
t('dialog.avatar.actions.change_image')
}}</el-dropdown-item>
@@ -482,6 +505,7 @@
</el-tabs>
</div>
<SetAvatarTagsDialog :set-avatar-tags-dialog="setAvatarTagsDialog" />
<SetAvatarStylesDialog :set-avatar-styles-dialog="setAvatarStylesDialog" />
</el-dialog>
</template>
@@ -494,6 +518,7 @@
import $utils from '../../../classes/utils';
import SetAvatarTagsDialog from './SetAvatarTagsDialog.vue';
import SetAvatarStylesDialog from './SetAvatarStylesDialog.vue';
const API = inject('API');
const beforeDialogClose = inject('beforeDialogClose');
@@ -550,6 +575,17 @@
contentAdult: false,
contentSex: false
});
const setAvatarStylesDialog = reactive({
visible: false,
loading: false,
avatarId: '',
initialPrimaryStyle: '',
initialSecondaryStyle: '',
primaryStyle: '',
secondaryStyle: '',
availableAvatarStyles: [],
availableAvatarStylesMap: new Map()
});
const avatarDialogPlatform = computed(() => {
const { ref } = props.avatarDialog;
@@ -653,6 +689,9 @@
case 'Change Content Tags':
showSetAvatarTagsDialog(D.id);
break;
case 'Change Styles':
showSetAvatarStylesDialog(D.id);
break;
case 'Download Unity Package':
openExternalLink(utils.replaceVrcPackageUrl(props.avatarDialog.ref.unityPackageUrl));
break;
@@ -894,7 +933,7 @@
memo: memo.value
});
} else {
database.deleteAvatarMemo(props.avatarDialog.avatarId);
database.deleteAvatarMemo(props.avatarDialog.id);
}
}
@@ -1049,6 +1088,20 @@
});
}
function showSetAvatarStylesDialog() {
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.initialPrimaryStyle = D.primaryStyle;
D.initialSecondaryStyle = D.secondaryStyle;
nextTick(() => {
D.loading = false;
});
}
function downloadAndSaveJson(fileName, data) {
utils.downloadAndSaveJson(fileName, data);
}

View File

@@ -0,0 +1,130 @@
<template>
<el-dialog
ref="setAvatarStylesDialog"
class="x-dialog"
:before-close="beforeDialogClose"
:visible.sync="setAvatarStylesDialog.visible"
:title="t('dialog.set_avatar_styles.header')"
width="400px"
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<template v-if="setAvatarStylesDialog.visible">
<div>
<span>{{ t('dialog.set_avatar_styles.primary_style') }}</span>
<el-select
v-model="setAvatarStylesDialog.primaryStyle"
:placeholder="t('dialog.set_avatar_styles.select_style')"
size="small"
clearable
style="display: inline-block">
<el-option
v-for="(style, index) in setAvatarStylesDialog.availableAvatarStyles"
:key="index"
:label="style"
:value="style"></el-option>
</el-select>
</div>
<br />
<div>
<span>{{ t('dialog.set_avatar_styles.secondary_style') }}</span>
<el-select
v-model="setAvatarStylesDialog.secondaryStyle"
:placeholder="t('dialog.set_avatar_styles.select_style')"
size="small"
clearable
style="display: inline-block">
<el-option
v-for="(style, index) in setAvatarStylesDialog.availableAvatarStyles"
:key="index"
:label="style"
:value="style"></el-option>
</el-select>
</div>
</template>
<template #footer>
<el-button size="small" @click="setAvatarStylesDialog.visible = false">{{
t('dialog.set_avatar_styles.cancel')
}}</el-button>
<el-button type="primary" size="small" @click="saveSetAvatarStylesDialog">{{
t('dialog.set_avatar_styles.save')
}}</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { inject, watch, getCurrentInstance } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import { avatarRequest } from '../../../classes/request';
const beforeDialogClose = inject('beforeDialogClose');
const dialogMouseDown = inject('dialogMouseDown');
const dialogMouseUp = inject('dialogMouseUp');
const { t } = useI18n();
const instance = getCurrentInstance();
const $message = instance.proxy.$message;
const props = defineProps({
setAvatarStylesDialog: {
type: Object,
required: true
}
});
watch(
() => props.setAvatarStylesDialog.visible,
(newVal) => {
if (newVal) {
getAvatarStyles();
}
}
);
async function getAvatarStyles() {
const ref = await avatarRequest.getAvailableAvatarStyles();
const styles = [];
const stylesMap = new Map();
for (const style of ref.json) {
styles.push(style.styleName);
stylesMap.set(style.styleName, style.id);
}
props.setAvatarStylesDialog.availableAvatarStyles = styles;
props.setAvatarStylesDialog.availableAvatarStylesMap = stylesMap;
}
function saveSetAvatarStylesDialog() {
if (
props.setAvatarStylesDialog.initialPrimaryStyle === props.setAvatarStylesDialog.primaryStyle &&
props.setAvatarStylesDialog.initialSecondaryStyle === props.setAvatarStylesDialog.secondaryStyle
) {
props.setAvatarStylesDialog.visible = false;
return;
}
const primaryStyleId =
props.setAvatarStylesDialog.availableAvatarStylesMap.get(props.setAvatarStylesDialog.primaryStyle) || '';
const secondaryStyleId =
props.setAvatarStylesDialog.availableAvatarStylesMap.get(props.setAvatarStylesDialog.secondaryStyle) || '';
const params = {
id: props.setAvatarStylesDialog.avatarId,
primaryStyle: primaryStyleId,
secondaryStyle: secondaryStyleId
};
avatarRequest
.saveAvatar(params)
.then(() => {
$message.success(t('dialog.set_avatar_styles.save_success'));
props.setAvatarStylesDialog.visible = false;
})
.catch((error) => {
$message.error(t('dialog.set_avatar_styles.save_failed'));
console.error('Error saving avatar styles:', error);
});
}
</script>
<style scoped></style>

View File

@@ -177,15 +177,15 @@
D.selectedCount++;
}
ref.$tagString = '';
const conentTags = [];
const contentTags = [];
ref.tags.forEach((tag) => {
if (tag.startsWith('content_')) {
conentTags.push(tag.substring(8));
contentTags.push(tag.substring(8));
}
});
for (let i = 0; i < conentTags.length; ++i) {
const tag = conentTags[i];
if (i < conentTags.length - 1) {
for (let i = 0; i < contentTags.length; ++i) {
const tag = contentTags[i];
if (i < contentTags.length - 1) {
ref.$tagString += `${tag}, `;
} else {
ref.$tagString += tag;