fix: debug with type error checks

This commit is contained in:
pa
2025-07-16 14:23:06 +09:00
committed by Natsumi
parent 0e50a67c25
commit b23687430e
13 changed files with 217 additions and 194 deletions

View File

@@ -87,8 +87,7 @@ const worldReq = {
}, },
/** /**
* @param {{id: string}} params * @type {import('../types/api/world').SaveWorld}
* @returns {Promise<{json: any, params}>}
*/ */
saveWorld(params) { saveWorld(params) {
const worldStore = useWorldStore(); const worldStore = useWorldStore();

View File

@@ -60,7 +60,7 @@
} }
if (Array.isArray(props.avatartags)) { if (Array.isArray(props.avatartags)) {
avatarTags.value = props.avatartags.map((tag) => tag.replace('content_', '')).join(', '); avatarTags.value = props.avatartags.map((tag) => String(tag).replace('content_', '')).join(', ');
} }
}; };

View File

@@ -86,7 +86,7 @@
capacity: 0, capacity: 0,
queueSize: 0, queueSize: 0,
queueEnabled: false, queueEnabled: false,
platforms: [], platforms: {},
userList: [], userList: [],
gameServerVersion: '', gameServerVersion: '',
canCloseInstance: false, canCloseInstance: false,

View File

@@ -48,32 +48,32 @@
<br /> <br />
<div <div
class="x-friend-item" class="x-friend-item"
v-if="image.versions && image.versions.length > 0"
v-for="image in galleryTable" v-for="image in galleryTable"
:key="image.id" :key="image.id"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default"> style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
<div <template v-if="image.versions && image.versions.length > 0">
class="vrcplus-icon" <div
v-if="image.versions[image.versions.length - 1].file.url" class="vrcplus-icon"
@click="setProfilePicOverride(image.id)" v-if="image.versions[image.versions.length - 1].file.url"
:class="{ 'current-vrcplus-icon': compareCurrentProfilePic(image.id) }"> @click="setProfilePicOverride(image.id)"
<img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" /> :class="{ 'current-vrcplus-icon': compareCurrentProfilePic(image.id) }">
</div> <img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" />
<div style="float: right; margin-top: 5px"> </div>
<el-button <div style="float: right; margin-top: 5px">
type="default" <el-button
@click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" type="default"
size="mini" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"
icon="el-icon-picture-outline" size="mini"
circle></el-button> icon="el-icon-picture-outline"
<el-button circle></el-button>
type="default" <el-button
@click="deleteGalleryImage(image.id)" type="default"
size="mini" @click="deleteGalleryImage(image.id)"
icon="el-icon-delete" size="mini"
circle icon="el-icon-delete"
style="margin-left: 5px"></el-button> circle
</div> style="margin-left: 5px"></el-button></div
></template>
</div> </div>
</el-tab-pane> </el-tab-pane>
@@ -117,32 +117,32 @@
<br /> <br />
<div <div
class="x-friend-item" class="x-friend-item"
v-if="image.versions && image.versions.length > 0"
v-for="image in VRCPlusIconsTable" v-for="image in VRCPlusIconsTable"
:key="image.id" :key="image.id"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default"> style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
<div <template v-if="image.versions && image.versions.length > 0"
class="vrcplus-icon" ><div
v-if="image.versions[image.versions.length - 1].file.url" class="vrcplus-icon"
@click="setVRCPlusIcon(image.id)" v-if="image.versions[image.versions.length - 1].file.url"
:class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(image.id) }"> @click="setVRCPlusIcon(image.id)"
<img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" /> :class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(image.id) }">
</div> <img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" />
<div style="float: right; margin-top: 5px"> </div>
<el-button <div style="float: right; margin-top: 5px">
type="default" <el-button
@click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" type="default"
size="mini" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"
icon="el-icon-picture-outline" size="mini"
circle></el-button> icon="el-icon-picture-outline"
<el-button circle></el-button>
type="default" <el-button
@click="deleteVRCPlusIcon(image.id)" type="default"
size="mini" @click="deleteVRCPlusIcon(image.id)"
icon="el-icon-delete" size="mini"
circle icon="el-icon-delete"
style="margin-left: 5px"></el-button> circle
</div> style="margin-left: 5px"></el-button></div
></template>
</div> </div>
</el-tab-pane> </el-tab-pane>
@@ -226,65 +226,67 @@
<br /> <br />
<div <div
class="x-friend-item" class="x-friend-item"
v-if="image.versions && image.versions.length > 0"
v-for="image in emojiTable" v-for="image in emojiTable"
:key="image.id" :key="image.id"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default"> style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
<div <template v-if="image.versions && image.versions.length > 0">
class="vrcplus-icon" <div
v-if="image.versions[image.versions.length - 1].file.url" class="vrcplus-icon"
style="overflow: hidden" v-if="image.versions[image.versions.length - 1].file.url"
@click=" style="overflow: hidden"
showFullscreenImageDialog(
image.versions[image.versions.length - 1].file.url,
getEmojiFileName(image)
)
">
<template v-if="image.frames">
<div
class="avatar"
:style="
generateEmojiStyle(
image.versions[image.versions.length - 1].file.url,
image.framesOverTime,
image.frames,
image.loopStyle
)
"></div>
</template>
<template v-else>
<img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" />
</template>
</div>
<div style="display: inline-block; margin: 5px">
<span v-if="image.loopStyle === 'pingpong'">
<i class="el-icon-refresh el-icon--left"></i>
</span>
<span style="margin-right: 5px">{{ image.animationStyle }}</span>
<span v-if="image.framesOverTime" style="margin-right: 5px">{{ image.framesOverTime }}fps</span>
<span v-if="image.frames" style="margin-right: 5px">{{ image.frames }}frames</span>
<br />
</div>
<div style="float: right; margin-top: 5px">
<el-button
type="default"
@click=" @click="
showFullscreenImageDialog( showFullscreenImageDialog(
image.versions[image.versions.length - 1].file.url, image.versions[image.versions.length - 1].file.url,
getEmojiFileName(image) getEmojiFileName(image)
) )
" ">
size="mini" <template v-if="image.frames">
icon="el-icon-picture-outline" <div
circle></el-button> class="avatar"
<el-button :style="
type="default" generateEmojiStyle(
@click="deleteEmoji(image.id)" image.versions[image.versions.length - 1].file.url,
size="mini" image.framesOverTime,
icon="el-icon-delete" image.frames,
circle image.loopStyle
style="margin-left: 5px"></el-button> )
</div> "></div>
</template>
<template v-else>
<img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" />
</template>
</div>
<div style="display: inline-block; margin: 5px">
<span v-if="image.loopStyle === 'pingpong'">
<i class="el-icon-refresh el-icon--left"></i>
</span>
<span style="margin-right: 5px">{{ image.animationStyle }}</span>
<span v-if="image.framesOverTime" style="margin-right: 5px"
>{{ image.framesOverTime }}fps</span
>
<span v-if="image.frames" style="margin-right: 5px">{{ image.frames }}frames</span>
<br />
</div>
<div style="float: right; margin-top: 5px">
<el-button
type="default"
@click="
showFullscreenImageDialog(
image.versions[image.versions.length - 1].file.url,
getEmojiFileName(image)
)
"
size="mini"
icon="el-icon-picture-outline"
circle></el-button>
<el-button
type="default"
@click="deleteEmoji(image.id)"
size="mini"
icon="el-icon-delete"
circle
style="margin-left: 5px"></el-button></div
></template>
</div> </div>
</el-tab-pane> </el-tab-pane>
@@ -320,32 +322,32 @@
<br /> <br />
<div <div
class="x-friend-item" class="x-friend-item"
v-if="image.versions && image.versions.length > 0"
v-for="image in stickerTable" v-for="image in stickerTable"
:key="image.id" :key="image.id"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default"> style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
<div <template v-if="image.versions && image.versions.length > 0">
class="vrcplus-icon" <div
v-if="image.versions[image.versions.length - 1].file.url" class="vrcplus-icon"
style="overflow: hidden" v-if="image.versions[image.versions.length - 1].file.url"
@click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"> style="overflow: hidden"
<img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" /> @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)">
</div> <img class="avatar" v-lazy="image.versions[image.versions.length - 1].file.url" />
<div style="float: right; margin-top: 5px"> </div>
<el-button <div style="float: right; margin-top: 5px">
type="default" <el-button
@click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)" type="default"
size="mini" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"
icon="el-icon-picture-outline" size="mini"
circle></el-button> icon="el-icon-picture-outline"
<el-button circle></el-button>
type="default" <el-button
@click="deleteSticker(image.id)" type="default"
size="mini" @click="deleteSticker(image.id)"
icon="el-icon-delete" size="mini"
circle icon="el-icon-delete"
style="margin-left: 5px"></el-button> circle
</div> style="margin-left: 5px"></el-button></div
></template>
</div> </div>
</el-tab-pane> </el-tab-pane>

View File

@@ -79,7 +79,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, nextTick, watch, getCurrentInstance, onMounted } from 'vue'; import { ref, computed, nextTick, watch, getCurrentInstance } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n-bridge'; import { useI18n } from 'vue-i18n-bridge';
import { instanceRequest, worldRequest } from '../../api'; import { instanceRequest, worldRequest } from '../../api';

View File

@@ -101,23 +101,25 @@
<el-option-group :label="t('dialog.new_instance.group_placeholder')"> <el-option-group :label="t('dialog.new_instance.group_placeholder')">
<el-option <el-option
v-for="group in currentUserGroups.values()" v-for="group in currentUserGroups.values()"
v-if="
group &&
(hasGroupPermission(group, 'group-instance-public-create') ||
hasGroupPermission(group, 'group-instance-plus-create') ||
hasGroupPermission(group, 'group-instance-open-create'))
"
:key="group.id" :key="group.id"
:label="group.name" :label="group.name"
:value="group.id" :value="group.id"
class="x-friend-item" class="x-friend-item"
style="height: auto; width: 478px"> style="height: auto; width: 478px">
<div class="avatar"> <template
<img v-lazy="group.iconUrl" /> v-if="
</div> group &&
<div class="detail"> (hasGroupPermission(group, 'group-instance-public-create') ||
<span class="name" v-text="group.name"></span> hasGroupPermission(group, 'group-instance-plus-create') ||
</div> hasGroupPermission(group, 'group-instance-open-create'))
">
<div class="avatar">
<img v-lazy="group.iconUrl" />
</div>
<div class="detail">
<span class="name" v-text="group.name"></span>
</div>
</template>
</el-option> </el-option>
</el-option-group> </el-option-group>
</el-select> </el-select>
@@ -362,18 +364,18 @@
<el-option-group :label="t('dialog.new_instance.group_placeholder')"> <el-option-group :label="t('dialog.new_instance.group_placeholder')">
<el-option <el-option
v-for="group in currentUserGroups.values()" v-for="group in currentUserGroups.values()"
v-if="group"
:key="group.id" :key="group.id"
class="x-friend-item" class="x-friend-item"
:label="group.name" :label="group.name"
:value="group.id" :value="group.id"
style="height: auto; width: 478px"> style="height: auto; width: 478px">
<div class="avatar"> <template v-if="group">
<img v-lazy="group.iconUrl" /> <div class="avatar">
</div> <img v-lazy="group.iconUrl" />
<div class="detail"> </div>
<span class="name" v-text="group.name"></span> <div class="detail">
</div> <span class="name" v-text="group.name"></span></div
></template>
</el-option> </el-option>
</el-option-group> </el-option-group>
</el-select> </el-select>

View File

@@ -7,12 +7,13 @@
append-to-body append-to-body
@close="closeDialog"> @close="closeDialog">
<div> <div>
<div <div v-for="image in previousImagesTable" :key="image.version" style="display: inline-block">
v-for="image in previousImagesTable" <el-popover
v-if="image.file" class="x-change-image-item"
:key="image.version" placement="right"
style="display: inline-block"> width="500px"
<el-popover class="x-change-image-item" placement="right" width="500px" trigger="click"> trigger="click"
v-if="image.file">
<img slot="reference" v-lazy="image.file.url" class="x-link" /> <img slot="reference" v-lazy="image.file.url" class="x-link" />
<img <img
v-lazy="image.file.url" v-lazy="image.file.url"

View File

@@ -44,7 +44,7 @@
{{ t('dialog.vrcx_updater.cancel') }} {{ t('dialog.vrcx_updater.cancel') }}
</el-button> </el-button>
<el-button <el-button
v-if="VRCXUpdateDialog.release !== pendingVRCXInstall" v-if="Boolean(VRCXUpdateDialog.release) !== pendingVRCXInstall"
:disabled="updateInProgress" :disabled="updateInProgress"
type="primary" type="primary"
size="small" size="small"

View File

@@ -77,7 +77,8 @@
base64SignatureFile: '', base64SignatureFile: '',
signatureMd5: '', signatureMd5: '',
fileId: '', fileId: '',
avatarId: '' avatarId: '',
worldId: ''
}); });
function uploadWorldImage() { function uploadWorldImage() {
@@ -114,8 +115,9 @@
function onFileChangeWorldImage(e) { function onFileChangeWorldImage(e) {
const clearFile = function () { const clearFile = function () {
if (document.querySelector('#WorldImageUploadButton')) { const fileInput = /** @type {HTMLInputElement} */ (document.querySelector('#WorldImageUploadButton'));
document.querySelector('#WorldImageUploadButton').value = ''; if (fileInput) {
fileInput.value = '';
} }
}; };
const files = e.target.files || e.dataTransfer.files; const files = e.target.files || e.dataTransfer.files;
@@ -144,10 +146,10 @@
const r = new FileReader(); const r = new FileReader();
r.onload = async function (file) { r.onload = async function (file) {
try { try {
const base64File = await resizeImageToFitLimits(btoa(r.result)); const base64File = await resizeImageToFitLimits(btoa(r.result.toString()));
// 10MB // 10MB
const fileMd5 = await genMd5(base64File); const fileMd5 = await genMd5(base64File);
const fileSizeInBytes = parseInt(file.total, 10); const fileSizeInBytes = parseInt(file.total.toString(), 10);
const base64SignatureFile = await genSig(base64File); const base64SignatureFile = await genSig(base64File);
const signatureMd5 = await genMd5(base64SignatureFile); const signatureMd5 = await genMd5(base64SignatureFile);
const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10); const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10);
@@ -168,7 +170,8 @@
base64SignatureFile, base64SignatureFile,
signatureMd5, signatureMd5,
fileId, fileId,
worldId worldId,
...worldImage.value
}; };
const params = { const params = {
fileMd5, fileMd5,
@@ -231,7 +234,7 @@
if (json.status !== 200) { if (json.status !== 200) {
changeWorldImageDialogLoading.value = false; changeWorldImageDialogLoading.value = false;
$throw('World image upload failed', json, params.url); $throw(json.status, 'World image upload failed', params.url);
} }
const args = { const args = {
json, json,
@@ -284,7 +287,7 @@
if (json.status !== 200) { if (json.status !== 200) {
changeWorldImageDialogLoading.value = false; changeWorldImageDialogLoading.value = false;
$throw('World image upload failed', json, params.url); $throw(json.status, 'World image upload failed', params.url);
} }
const args = { const args = {
json, json,

View File

@@ -73,7 +73,7 @@
</el-checkbox> </el-checkbox>
<template #footer> <template #footer>
<div style="display: flex"> <div style="display: flex">
<el-button size="small" @click="setWorldTagsDialog.visible = false"> <el-button size="small" @click="isVisible = false">
{{ t('dialog.set_world_tags.cancel') }} {{ t('dialog.set_world_tags.cancel') }}
</el-button> </el-button>
<el-button type="primary" size="small" @click="saveSetWorldTagsDialog"> <el-button type="primary" size="small" @click="saveSetWorldTagsDialog">
@@ -168,13 +168,13 @@
const authorTags = []; const authorTags = [];
const contentTags = []; const contentTags = [];
props.oldTags.forEach((tag) => { props.oldTags.forEach((tag) => {
if (tag.startsWith('author_tag_')) { if (String(tag).startsWith('author_tag_')) {
authorTags.unshift(tag.substring(11)); authorTags.unshift(String(tag).substring(11));
} }
if (tag.startsWith('content_')) { if (String(tag).startsWith('content_')) {
contentTags.unshift(tag.substring(8)); contentTags.unshift(String(tag).substring(8));
} }
switch (tag) { switch (String(tag)) {
case 'content_horror': case 'content_horror':
D.contentHorror = true; D.contentHorror = true;
break; break;

View File

@@ -723,10 +723,10 @@
style="margin-left: 5px" style="margin-left: 5px"
@click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button> @click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button>
<el-tree :data="treeData" style="margin-top: 5px; font-size: 12px"> <el-tree :data="treeData" style="margin-top: 5px; font-size: 12px">
<template slot-scope="scope"> <template #default="{ data }">
<span> <span>
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span> <span style="font-weight: bold; margin-right: 5px" v-text="data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span> <span v-if="!data.children" v-text="data.value"></span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
@@ -856,7 +856,8 @@
const timeInLab = computed(() => { const timeInLab = computed(() => {
return timeToText( return timeToText(
new Date(worldDialog.value.ref.publicationDate) - new Date(worldDialog.value.ref.labsPublicationDate) new Date(worldDialog.value.ref.publicationDate).getTime() -
new Date(worldDialog.value.ref.labsPublicationDate).getTime()
); );
}); });
@@ -1194,7 +1195,7 @@
worldRequest worldRequest
.saveWorld({ .saveWorld({
id: world.id, id: world.id,
capacity: instance.inputValue capacity: Number(instance.inputValue)
}) })
.then((args) => { .then((args) => {
proxy.$message({ proxy.$message({
@@ -1224,7 +1225,7 @@
worldRequest worldRequest
.saveWorld({ .saveWorld({
id: world.id, id: world.id,
recommendedCapacity: instance.inputValue recommendedCapacity: Number(instance.inputValue)
}) })
.then((args) => { .then((args) => {
proxy.$message({ proxy.$message({

View File

@@ -46,6 +46,27 @@ export type GetWorlds = (
option?: string; option?: string;
}>; }>;
export type SaveWorld = (params: {
id: string;
name?: string;
description?: string;
capacity?: number;
recommendedCapacity?: number;
previewYoutubeId?: string;
urlList?: string[];
tags?: string[];
}) => Promise<{
json: SaveWorldResponse;
params: {
id: string;
name?: string;
description?: string;
capacity?: number;
recommendedCapacity?: number;
previewYoutubeId?: string;
};
}>;
// Type aliases // Type aliases
type WorldSearchResponse = WorldSearchResponseItem[]; type WorldSearchResponse = WorldSearchResponseItem[];
@@ -67,3 +88,20 @@ interface GetWorldResponse extends BaseWorld {
version: number; version: number;
visits: number; visits: number;
} }
interface SaveWorldResponse extends BaseWorld {
description: string;
featured: boolean;
pendingUpload: boolean;
tags: string[];
thumbnailImageUrl: string;
imageUrl: string;
name: string;
authorId: string;
authorName: string;
id: string;
updated_at: string;
urlList: string[];
version: number;
visits: number;
}

View File

@@ -398,29 +398,6 @@ declare global {
data?: any; data?: any;
}): Promise<{ status: number; data: string }>; }): Promise<{ status: number; data: string }>;
}; };
const electron: {
openFileDialog: () => Promise<string>;
openDirectoryDialog: () => Promise<string>;
desktopNotification: (
displayName: string,
body?: string,
image?: string
) => Promise<void>;
onWindowPositionChanged: (
Function: (event: any, position: { x: number; y: number }) => void
) => void;
onWindowSizeChanged: (
Function: (
event: any,
size: { width: number; height: number }
) => void
) => void;
onWindowStateChange: (
Function: (event: any, state: { windowState: any }) => void
) => void;
restartApp: () => Promise<void>;
};
} }
export {}; export {};