inventory and prop support

This commit is contained in:
Natsumi
2025-06-17 08:16:34 +12:00
parent adea1c083f
commit eaca05a485
10 changed files with 263 additions and 6 deletions

View File

@@ -21,6 +21,8 @@ import inviteMessagesRequest from './inviteMessages';
import imageRequest from './image';
import miscRequest from './misc';
import groupRequest from './group';
import inventoryRequest from './inventory';
import propRequest from './prop';
window.request = {
userRequest,
@@ -37,7 +39,9 @@ window.request = {
inviteMessagesRequest,
imageRequest,
miscRequest,
groupRequest
groupRequest,
inventoryRequest,
propRequest
};
export {
@@ -55,5 +59,7 @@ export {
inviteMessagesRequest,
imageRequest,
miscRequest,
groupRequest
groupRequest,
inventoryRequest,
propRequest
};

74
src/api/inventory.js Normal file
View File

@@ -0,0 +1,74 @@
const inventoryReq = {
/**
* @param {{ inventoryId: string }} params
* @returns {Promise<{json: any, params}>}
*/
getInventoryItem(params) {
return window.API.call(`inventory/${params.inventoryId}`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ n: number, offset: number, order: string, types: string }} params
* @returns {Promise<{json: any, params}>}
*/
getInventoryItems(params) {
return window.API.call('inventory', {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ inventoryId: string }} params
* @returns {Promise<{json: any, params}>}
*/
consumeInventoryBundle(params) {
return window.API.call(`inventory/${params.inventoryId}/consume`, {
method: 'PUT',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ inventoryTemplateId: string }} params
* @returns {Promise<{json: any, params}>}
*/
getInventoryTemplate(params) {
return window.API.call(
`inventory/template/${params.inventoryTemplateId}`,
{
method: 'GET',
params
}
).then((json) => {
const args = {
json,
params
};
return args;
});
}
};
export default inventoryReq;

20
src/api/prop.js Normal file
View File

@@ -0,0 +1,20 @@
const propReq = {
/**
* @param {{ propId: string }} params
* @returns {Promise<{json: any, params}>}
*/
getProp(params) {
return window.API.call(`props/${params.propId}`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
}
};
export default propReq;

View File

@@ -153,6 +153,7 @@ import _languages from './classes/languages.js';
import _groups from './classes/groups.js';
import _vrcRegistry from './classes/vrcRegistry.js';
import _restoreFriendOrder from './classes/restoreFriendOrder.js';
import _inventory from './classes/inventory.js';
import { userNotes } from './classes/userNotes.js';
@@ -244,7 +245,8 @@ console.log(`isLinux: ${LINUX}`);
languages: new _languages($app, API, $t),
groups: new _groups($app, API, $t),
vrcRegistry: new _vrcRegistry($app, API, $t),
restoreFriendOrder: new _restoreFriendOrder($app, API, $t)
restoreFriendOrder: new _restoreFriendOrder($app, API, $t),
inventory: new _inventory($app, API, $t)
};
await configRepository.init();
@@ -2014,6 +2016,7 @@ console.log(`isLinux: ${LINUX}`);
this.cachedFavoriteGroups.clear();
this.cachedFavoriteGroupsByTypeName.clear();
this.currentUserGroups.clear();
this.currentUserInventory.clear();
this.queuedInstances.clear();
this.favoriteFriendGroups = [];
this.favoriteWorldGroups = [];
@@ -9859,7 +9862,8 @@ console.log(`isLinux: ${LINUX}`);
'stickers',
'pedestals',
'prints',
'drones'
'drones',
'items'
];
$app.methods.createNewInstance = async function (worldId = '', options) {
@@ -10803,6 +10807,7 @@ console.log(`isLinux: ${LINUX}`);
$app.data.galleryDialogEmojisLoading = false;
$app.data.galleryDialogStickersLoading = false;
$app.data.galleryDialogPrintsLoading = false;
$app.data.galleryDialogInventoryLoading = false;
API.$on('LOGIN', function () {
$app.galleryTable = [];
@@ -10815,6 +10820,7 @@ console.log(`isLinux: ${LINUX}`);
this.refreshEmojiTable();
this.refreshStickerTable();
this.refreshPrintTable();
this.getInventory();
workerTimers.setTimeout(() => this.setGalleryTab(pageNum), 100);
};

55
src/classes/inventory.js Normal file
View File

@@ -0,0 +1,55 @@
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;
}
}
};
}

View File

@@ -559,6 +559,15 @@ export default class extends baseClass {
// 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',

View File

@@ -444,6 +444,48 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane v-loading="galleryDialogInventoryLoading" lazy>
<span slot="label">
{{ t('dialog.gallery_icons.inventory') }}
<span style="color: #909399; font-size: 12px; margin-left: 5px"> {{ inventoryTable.length }} </span>
</span>
<br />
<br />
<div
class="x-friend-item"
v-for="item in inventoryTable"
:key="item.id"
style="display: inline-block; margin-top: 10px; width: unset; cursor: default">
<div class="vrcplus-icon" style="overflow: hidden; cursor: default">
<img class="avatar" v-lazy="item.imageUrl" />
</div>
<div style="margin-top: 5px; width: 208px">
<span class="x-ellipsis" v-text="item.name" style="display: block"></span>
<span
v-if="item.description"
class="x-ellipsis"
v-text="item.description"
style="display: block"></span>
<span v-else style="display: block">&nbsp;</span>
<span
class="x-ellipsis"
style="color: #909399; font-family: monospace; font-size: 11px; display: block">
{{ item.created_at | formatDate('long') }}
</span>
<span v-text="item.itemType" style="display: block"></span>
</div>
<el-button
v-if="item.itemType === 'bundle'"
type="default"
@click="consumeInventoryBundle(item.id)"
size="mini"
icon="el-icon-plus"
circle>
{{ t('dialog.gallery_icons.consume_bundle') }}
</el-button>
</div>
</el-tab-pane>
</el-tabs>
</safe-dialog>
</template>
@@ -451,7 +493,7 @@
<script setup>
import { getCurrentInstance, inject, ref } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import { userRequest, vrcPlusIconRequest, vrcPlusImageRequest, miscRequest } from '../../../api';
import { userRequest, vrcPlusIconRequest, vrcPlusImageRequest, miscRequest, inventoryRequest } from '../../../api';
import { extractFileId } from '../../../composables/shared/utils';
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../../composables/user/constants/emoji';
import { getPrintFileName } from '../../../composables/user/utils';
@@ -490,6 +532,10 @@
type: Boolean,
required: true
},
galleryDialogInventoryLoading: {
type: Boolean,
required: true
},
galleryTable: {
type: Array,
required: true
@@ -518,6 +564,10 @@
printTable: {
type: Array,
required: true
},
inventoryTable: {
type: Array,
required: true
}
});
@@ -1025,4 +1075,27 @@
}
});
}
async function consumeInventoryBundle(inventoryId) {
try {
const args = await inventoryRequest.consumeInventoryBundle({
inventoryId
});
API.currentUserInventory.delete(inventoryId);
const array = props.inventoryTable;
const { length } = array;
for (let i = 0; i < length; ++i) {
if (inventoryId === array[i].id) {
array.splice(i, 1);
break;
}
}
this.getInventory();
} catch (error) {
console.error('Error consuming inventory bundle:', error);
}
// errors: []
// inventoryItems : []
// inventoryItemsCreated: 0
}
</script>

View File

@@ -1798,6 +1798,7 @@
:gallery-dialog-emojis-loading="galleryDialogEmojisLoading"
:gallery-dialog-stickers-loading="galleryDialogStickersLoading"
:gallery-dialog-prints-loading="galleryDialogPrintsLoading"
:gallery-dialog-inventory-loading="galleryDialogInventoryLoading"
:gallery-table="galleryTable"
:VRCPlusIconsTable="VRCPlusIconsTable"
:emoji-table="emojiTable"
@@ -1805,6 +1806,7 @@
:print-upload-note="printUploadNote"
:print-crop-border="printCropBorder"
:print-table="printTable"
:inventory-table="inventoryTable"
@refreshGalleryTable="refreshGalleryTable"
@refreshVRCPlusIconsTable="refreshVRCPlusIconsTable"
@refreshStickerTable="refreshStickerTable"
@@ -2002,6 +2004,10 @@
type: Boolean,
required: true
},
galleryDialogInventoryLoading: {
type: Boolean,
required: true
},
galleryTable: {
type: Array,
required: true
@@ -2030,6 +2036,10 @@
printTable: {
type: Array,
required: true
},
inventoryTable: {
type: Array,
required: true
}
});

View File

@@ -1444,6 +1444,7 @@
"emojis": "Emojis",
"stickers": "Stickers",
"prints": "Prints",
"inventory": "Inventory",
"refresh": "Refresh",
"upload": "Upload",
"clear": "Clear",
@@ -1454,7 +1455,8 @@
"emoji_loop_pingpong": "Loop PingPong",
"flipbook_info": "Select a 1024x1024 PNG spritesheet to use as an animated emoji, available frame grid sizes: 4, 16 or 64 (max FPS 64, max frames 64)",
"note": "Note",
"crop_print_border": "Crop Print Border"
"crop_print_border": "Crop Print Border",
"consume_bundle": "Consume"
},
"change_content_image": {
"avatar": "Change Avatar Image",

View File

@@ -88,6 +88,7 @@ mixin dialogs
:galleryDialogEmojisLoading='galleryDialogEmojisLoading'
:galleryDialogStickersLoading='galleryDialogStickersLoading'
:galleryDialogPrintsLoading='galleryDialogPrintsLoading'
:galleryDialogInventoryLoading='galleryDialogInventoryLoading'
:galleryTable='galleryTable'
:VRCPlusIconsTable='VRCPlusIconsTable'
:emojiTable='emojiTable'
@@ -95,6 +96,7 @@ mixin dialogs
:printUploadNote='printUploadNote'
:printCropBorder='printCropBorder'
:printTable='printTable'
:inventoryTable='inventoryTable'
@refreshGalleryTable='refreshGalleryTable'
@refreshEmojiTable='refreshEmojiTable'
@refreshStickerTable='refreshStickerTable'