mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-28 00:18:58 -07:00
[www] Fix room badge grants and profile badge lists (#148)
This commit is contained in:
@@ -1819,7 +1819,7 @@ function StaffPlayerActions({ account, navigate }: { account: PublicAccount; nav
|
||||
e.preventDefault()
|
||||
void itemGift.run(async () => {
|
||||
const res = await call<{
|
||||
kind: 'custom_item' | 'skin' | 'consumable'
|
||||
kind: 'custom_item' | 'badge' | 'skin' | 'consumable'
|
||||
name: string
|
||||
prefabName?: string
|
||||
owned?: number
|
||||
@@ -1829,6 +1829,8 @@ function StaffPlayerActions({ account, navigate }: { account: PublicAccount; nav
|
||||
})
|
||||
setItemId('')
|
||||
switch (res.kind) {
|
||||
case 'badge':
|
||||
return `Granted the badge “${res.name}” to @${account.username}.`
|
||||
case 'skin':
|
||||
return `Sent the skin “${res.name}” (${res.prefabName}) to @${account.username}.`
|
||||
case 'consumable':
|
||||
@@ -1840,12 +1842,12 @@ function StaffPlayerActions({ account, navigate }: { account: PublicAccount; nav
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
Gift item
|
||||
Gift item or badge
|
||||
<span className="staff-gift">
|
||||
<input
|
||||
type="text"
|
||||
value={itemId}
|
||||
placeholder="Custom item, skin or consumable id"
|
||||
placeholder="Badge, custom item, skin or consumable id"
|
||||
spellCheck={false}
|
||||
required
|
||||
onChange={(e) => setItemId(e.target.value)}
|
||||
@@ -1855,10 +1857,11 @@ function StaffPlayerActions({ account, navigate }: { account: PublicAccount; nav
|
||||
</button>
|
||||
</span>
|
||||
<span className="hint">
|
||||
A custom avatar item’s guid, a skin’s modification guid, a consumable’s item desc,
|
||||
or a store id — the kind is worked out from the id. Arrives as a gift box, as if
|
||||
bought from the store; free to them, and a creator isn’t paid. Consumables stack:
|
||||
send again for another.
|
||||
A badge or custom avatar item’s guid, a skin’s modification guid, a consumable’s
|
||||
item desc, or a store id — the kind is worked out from the id. Arrives as a gift
|
||||
box, as if bought from the store; free to them, and a creator isn’t paid.
|
||||
Consumables stack: send again for another. Badges must be published and can be
|
||||
granted while the player is offline.
|
||||
</span>
|
||||
</label>
|
||||
{itemGift.error && <p className="error">{itemGift.error}</p>}
|
||||
|
||||
@@ -1413,7 +1413,12 @@ async function giftCustomItem(
|
||||
return c.json({ error: 'This player already owns that item' }, 409)
|
||||
}
|
||||
|
||||
await grantCustomAvatarItem(c.env.DB, playerId, item.CustomAvatarItemId)
|
||||
if (item.OutfitType === 1000 && item.CurrentSaves.length === 0) {
|
||||
return c.json({ error: 'That badge has no published image' }, 400)
|
||||
}
|
||||
if (!(await grantCustomAvatarItem(c.env.DB, playerId, item.CustomAvatarItemId))) {
|
||||
return c.json({ error: 'This player already owns that item' }, 409)
|
||||
}
|
||||
const content = staffGiftContent({
|
||||
GiftContext: GIFT_CONTEXT_PURCHASED_GIFT_A,
|
||||
CustomAvatarItemId: item.CustomAvatarItemId,
|
||||
@@ -1439,7 +1444,7 @@ async function giftCustomItem(
|
||||
await announceGift(c, playerId, gift.id, content)
|
||||
|
||||
return c.json({
|
||||
kind: 'custom_item',
|
||||
kind: item.OutfitType === 1000 ? 'badge' : 'custom_item',
|
||||
playerId,
|
||||
customAvatarItemId: item.CustomAvatarItemId,
|
||||
name: item.Name,
|
||||
|
||||
@@ -37,6 +37,7 @@ import { generateToken } from '@repo/jwt'
|
||||
import {
|
||||
createCustomAvatarItem,
|
||||
SCHEMA_DDL as CUSTOM_AVATAR_ITEM_SCHEMA_DDL,
|
||||
importCustomAvatarItem,
|
||||
} from '../../../../api/src/custom-avatar-items-db'
|
||||
import {
|
||||
createReport,
|
||||
@@ -2637,6 +2638,77 @@ it('gifts a player a custom item in a gift box', async () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('grants a published room badge through profile gifting without room presence, once', async () => {
|
||||
await clearHub()
|
||||
await updateAccount(env.DB, 8530, { username: 'BadgeRecipient' })
|
||||
const seed = await seedCustomItem('0a1b2c3d-0000-4000-8000-000000000030', 8531)
|
||||
const badge = await importCustomAvatarItem(env.DB, {
|
||||
...seed,
|
||||
Name: 'Room Champion',
|
||||
OutfitType: 1000,
|
||||
CustomBadgeMetadata: { RoomId: 12345 },
|
||||
CurrentSaves: [
|
||||
{
|
||||
CustomAvatarItemSaveId: 1,
|
||||
CustomAvatarItemId: seed.CustomAvatarItemId,
|
||||
UnityAssetId: crypto.randomUUID(),
|
||||
BodyType: 0,
|
||||
OutfitType: 1000,
|
||||
QAState: 0,
|
||||
CreatedAt: seed.CreatedAt,
|
||||
ModifiedAt: seed.ModifiedAt,
|
||||
Description: null,
|
||||
ThumbnailFileName: 'badge.png',
|
||||
AdditionalConfiguration: '{}',
|
||||
UnityAsset: 'badge.png',
|
||||
UnityAssetHash: '',
|
||||
UnityAsset2: null,
|
||||
UnityAsset2Hash: null,
|
||||
},
|
||||
],
|
||||
})
|
||||
const responses = await Promise.all([
|
||||
giftItem(8530, badge.CustomAvatarItemId.toUpperCase(), 'Congratulations!'),
|
||||
giftItem(8530, badge.CustomAvatarItemId, 'Congratulations!'),
|
||||
])
|
||||
expect(responses.map((r) => r.status).sort((a, b) => a - b)).toEqual([200, 409])
|
||||
const body = await responses.find((r) => r.status === 200)!.json()
|
||||
expect(body).toMatchObject({ kind: 'badge', name: 'Room Champion' })
|
||||
await expect(ownedCustomAvatarItemIds(env.DB, 8530, [badge.CustomAvatarItemId])).resolves.toEqual(
|
||||
new Set([badge.CustomAvatarItemId])
|
||||
)
|
||||
const gifts = await getPendingGifts(env.DB, 8530)
|
||||
expect(gifts).toHaveLength(1)
|
||||
expect(gifts[0]).toMatchObject({
|
||||
CustomAvatarItemId: badge.CustomAvatarItemId,
|
||||
AvatarItemType: 0,
|
||||
GiftContext: 500,
|
||||
Message: 'Congratulations!',
|
||||
})
|
||||
const frames = await hubFrames()
|
||||
expect(frames).toHaveLength(1)
|
||||
expect(frames[0]).toMatchObject({
|
||||
playerId: 8530,
|
||||
notificationType: 31,
|
||||
data: { CustomAvatarItemId: badge.CustomAvatarItemId, Message: 'Congratulations!' },
|
||||
})
|
||||
expect(await auditRows('gift_custom_item', 8530)).toHaveLength(1)
|
||||
expect((await giftItem(8530, badge.CustomAvatarItemId)).status).toBe(409)
|
||||
const draft = await importCustomAvatarItem(env.DB, {
|
||||
...badge,
|
||||
CustomAvatarItemId: crypto.randomUUID(),
|
||||
Accessibility: 0,
|
||||
})
|
||||
expect((await giftItem(8530, draft.CustomAvatarItemId)).status).toBe(404)
|
||||
const unbuilt = await importCustomAvatarItem(env.DB, {
|
||||
...badge,
|
||||
CustomAvatarItemId: crypto.randomUUID(),
|
||||
CurrentSaves: [],
|
||||
})
|
||||
expect((await giftItem(8530, unbuilt.CustomAvatarItemId)).status).toBe(400)
|
||||
expect(await getPendingGifts(env.DB, 8530)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('refuses a custom item gift the store would refuse', async () => {
|
||||
await updateAccount(env.DB, 8340, { username: 'Picky' })
|
||||
const published = await seedCustomItem('0a1b2c3d-0000-4000-8000-000000000002', 8341)
|
||||
|
||||
Reference in New Issue
Block a user