diff --git a/packages/client-core/src/admin/common/variables/avatar.ts b/packages/client-core/src/admin/common/variables/avatar.ts index e56c51903a..c5409fd7b4 100644 --- a/packages/client-core/src/admin/common/variables/avatar.ts +++ b/packages/client-core/src/admin/common/variables/avatar.ts @@ -26,7 +26,7 @@ Ethereal Engine. All Rights Reserved. import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema' export interface AvatarColumn { - id: 'select' | 'id' | 'name' | 'owner' | 'thumbnail' | 'action' + id: 'select' | 'id' | 'name' | 'user' | 'thumbnail' | 'action' label: string | React.ReactElement minWidth?: number align?: 'right' @@ -35,7 +35,7 @@ export interface AvatarColumn { export const avatarColumns: AvatarColumn[] = [ { id: 'id', label: 'Id', minWidth: 65 }, { id: 'name', label: 'Name', minWidth: 65 }, - { id: 'owner', label: 'Owner', minWidth: 65 }, + { id: 'user', label: 'Owner', minWidth: 65 }, { id: 'thumbnail', label: 'Thumbnail', diff --git a/packages/client-core/src/admin/components/Avatars/AvatarTable.tsx b/packages/client-core/src/admin/components/Avatars/AvatarTable.tsx index 35d294b076..e84426a112 100755 --- a/packages/client-core/src/admin/components/Avatars/AvatarTable.tsx +++ b/packages/client-core/src/admin/components/Avatars/AvatarTable.tsx @@ -34,7 +34,7 @@ import Checkbox from '@etherealengine/ui/src/primitives/mui/Checkbox' import { useFind, useMutation } from '@etherealengine/engine/src/common/functions/FeathersHooks' import TableComponent from '../../common/Table' -import { AvatarColumn, AvatarData, avatarColumns } from '../../common/variables/avatar' +import { AvatarColumn, avatarColumns } from '../../common/variables/avatar' import styles from '../../styles/admin.module.scss' import AvatarDrawer, { AvatarDrawerMode } from './AvatarDrawer' @@ -83,56 +83,54 @@ const AvatarTable = ({ className, search, selectedAvatarIds, setSelectedAvatarId } } - const createData = (el: AvatarType): AvatarData => { - return { - el, - select: ( - <> - { - toggleSelection(el.id) - }} - /> - - ), - id: el.id, - name: el.name as string, - owner: el.userId, - thumbnail: ( - ({ + el, + select: ( + <> + { + toggleSelection(el.id) + }} /> - ), - action: ( - <> - { - avatarData.set(el) - openAvatarDrawer.set(true) - }} - > - {t('admin:components.common.view')} - - { - avatarId.set(el.id) - avatarName.set(el.name) - openConfirm.set(true) - }} - > - {t('admin:components.common.delete')} - - - ) - } - } + + ), + id: el.id, + name: el.name as string, + user: el.user?.name || '', + thumbnail: ( + + ), + action: ( + <> + { + avatarData.set(el) + openAvatarDrawer.set(true) + }} + > + {t('admin:components.common.view')} + + { + avatarId.set(el.id) + avatarName.set(el.name) + openConfirm.set(true) + }} + > + {t('admin:components.common.delete')} + + + ) + }) const submitRemoveAvatar = async () => { adminAvatarRemove(avatarId.value) diff --git a/packages/engine/src/schemas/user/avatar.schema.ts b/packages/engine/src/schemas/user/avatar.schema.ts index b5586b420f..fa0fa5e9fc 100644 --- a/packages/engine/src/schemas/user/avatar.schema.ts +++ b/packages/engine/src/schemas/user/avatar.schema.ts @@ -55,6 +55,7 @@ export const avatarSchema = Type.Object( format: 'uuid' }), project: Type.String(), + user: Type.Optional(Type.Any()), // avoid circular reference to `userSchema` which utilizes current `avatarSchema` modelResource: Type.Optional(Type.Ref(staticResourceSchema)), thumbnailResource: Type.Optional(Type.Ref(staticResourceSchema)), createdAt: Type.String({ format: 'date-time' }), @@ -64,7 +65,7 @@ export const avatarSchema = Type.Object( ) export type AvatarType = Static -export type AvatarDatabaseType = Omit +export type AvatarDatabaseType = Omit // Schema for creating new entries // export const avatarDataSchema = Type.Pick( diff --git a/packages/server-core/src/user/avatar/avatar.hooks.ts b/packages/server-core/src/user/avatar/avatar.hooks.ts index 21eb2c9b48..f422d5690a 100755 --- a/packages/server-core/src/user/avatar/avatar.hooks.ts +++ b/packages/server-core/src/user/avatar/avatar.hooks.ts @@ -96,8 +96,25 @@ const ensureUserAccessibleAvatars = async (context: HookContext) isPublic: true } } +} + +const sortByUserName = async (context: HookContext) => { + if (!context.params.query || !context.params.query.$sort?.['user']) return + + const userSort = context.params.query.$sort['user'] + delete context.params.query.$sort['user'] + + if (context.params.query.name) { + context.params.query[`${avatarPath}.name`] = context.params.query.name + delete context.params.query.name + } + + const query = context.service.createQuery(context.params) - return context + query.leftJoin(userPath, `${userPath}.id`, `${avatarPath}.userId`) + query.orderBy(`${userPath}.name`, userSort === 1 ? 'asc' : 'desc') + + context.params.knex = query } /** @@ -119,8 +136,6 @@ const removeAvatarResources = async (context: HookContext) => { } catch (err) { logger.error(err) } - - return context } /** @@ -164,7 +179,8 @@ export default { all: [() => schemaHooks.validateQuery(avatarQueryValidator), schemaHooks.resolveQuery(avatarQueryResolver)], find: [ iffElse(isAction('admin'), verifyScope('admin', 'admin'), ensureUserAccessibleAvatars), - discardQuery('action') + discardQuery('action'), + sortByUserName ], get: [], create: [ diff --git a/packages/server-core/src/user/avatar/avatar.resolvers.ts b/packages/server-core/src/user/avatar/avatar.resolvers.ts index d2c3328496..c0617e31bb 100644 --- a/packages/server-core/src/user/avatar/avatar.resolvers.ts +++ b/packages/server-core/src/user/avatar/avatar.resolvers.ts @@ -31,6 +31,7 @@ import { staticResourcePath } from '@etherealengine/engine/src/schemas/media/sta import { AvatarDatabaseType, AvatarQuery, AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema' import type { HookContext } from '@etherealengine/server-core/declarations' +import { userPath } from '@etherealengine/engine/src/schemas/user/user.schema' import { fromDateTimeSql, getDateTimeSql } from '../../util/datetime-sql' export const avatarResolver = resolve({ @@ -39,6 +40,11 @@ export const avatarResolver = resolve({ }) export const avatarExternalResolver = resolve({ + user: virtual(async (avatar, context) => { + if (avatar.userId) { + return context.app.service(userPath).get(avatar.userId) + } + }), modelResource: virtual(async (avatar, context) => { if (context.event !== 'removed' && avatar.modelResourceId) return context.app.service(staticResourcePath).get(avatar.modelResourceId)