Skip to content

Commit

Permalink
Changes for avatar service to use schema, knex & feathers 5 (#8261)
Browse files Browse the repository at this point in the history
* Changes for avatar service to use schema, knex & feathers 5

* Fixed error due to static resource schema

* Fixed issue with missing associations

* Removed static resource fetching as its already done in resolver

* Added migration file

* Added migration file

* Fixed ts errors
  • Loading branch information
hanzlamateen committed Jul 19, 2023
1 parent bf7ffcc commit 28dd289
Show file tree
Hide file tree
Showing 44 changed files with 761 additions and 482 deletions.
4 changes: 2 additions & 2 deletions packages/client-core/src/admin/common/variables/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'

export interface AvatarColumn {
id: 'select' | 'id' | 'name' | 'owner' | 'thumbnail' | 'action'
Expand Down Expand Up @@ -51,7 +51,7 @@ export const avatarColumns: AvatarColumn[] = [
]

export interface AvatarData {
el: AvatarInterface
el: AvatarType
select: JSX.Element
id: string
name: string | undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ import {
THUMBNAIL_HEIGHT,
THUMBNAIL_WIDTH
} from '@etherealengine/common/src/constants/AvatarConstants'
import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader'
import { AvatarRigComponent } from '@etherealengine/engine/src/avatar/components/AvatarAnimationComponent'
import { getOptionalComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { dispatchAction, getMutableState, useHookstate } from '@etherealengine/hyperflux'
import Box from '@etherealengine/ui/src/primitives/mui/Box'
import Button from '@etherealengine/ui/src/primitives/mui/Button'
Expand Down Expand Up @@ -81,7 +81,7 @@ enum ConfirmState {
interface Props {
open: boolean
mode: AvatarDrawerMode
selectedAvatar?: AvatarInterface
selectedAvatar?: AvatarType
onClose: () => void
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import ConfirmDialog from '@etherealengine/client-core/src/common/components/ConfirmDialog'
import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { getMutableState, useHookstate } from '@etherealengine/hyperflux'
import Box from '@etherealengine/ui/src/primitives/mui/Box'
import Checkbox from '@etherealengine/ui/src/primitives/mui/Checkbox'
Expand Down Expand Up @@ -63,7 +63,7 @@ const AvatarTable = ({ className, search, selectedAvatarIds, setSelectedAvatarId
const fieldOrder = useHookstate('asc')
const sortField = useHookstate('name')
const openAvatarDrawer = useHookstate(false)
const avatarData = useHookstate<AvatarInterface | null>(null)
const avatarData = useHookstate<AvatarType | null>(null)

const handlePageChange = (event: unknown, newPage: number) => {
AdminAvatarService.fetchAdminAvatars(newPage, search, sortField.value, fieldOrder.value)
Expand Down Expand Up @@ -97,7 +97,7 @@ const AvatarTable = ({ className, search, selectedAvatarIds, setSelectedAvatarId
}
}

const createData = (el: AvatarInterface): AvatarData => {
const createData = (el: AvatarType): AvatarData => {
return {
el,
select: (
Expand Down
19 changes: 9 additions & 10 deletions packages/client-core/src/admin/services/AvatarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ Ethereal Engine. All Rights Reserved.

import { Paginated } from '@feathersjs/feathers'

import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AvatarResult } from '@etherealengine/common/src/interfaces/AvatarResult'
import { StaticResourceInterface } from '@etherealengine/common/src/interfaces/StaticResourceInterface'
import multiLogger from '@etherealengine/common/src/logger'
import { matches, Validator } from '@etherealengine/engine/src/common/functions/MatchesUtils'
import { avatarPath, AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { defineAction, defineState, dispatchAction, getMutableState } from '@etherealengine/hyperflux'

import { API } from '../../API'
Expand All @@ -42,7 +41,7 @@ export const AVATAR_PAGE_LIMIT = 100
export const AdminAvatarState = defineState({
name: 'AdminAvatarState',
initial: () => ({
avatars: [] as Array<AvatarInterface>,
avatars: [] as Array<AvatarType>,
thumbnail: undefined as StaticResourceInterface | undefined,
skip: 0,
limit: AVATAR_PAGE_LIMIT,
Expand Down Expand Up @@ -92,14 +91,14 @@ export const AdminAvatarReceptors = {

//Service
export const AdminAvatarService = {
fetchAdminAvatars: async (skip = 0, search: string | null = null, sortField = 'name', orderBy = 'asc') => {
let sortData = {}
fetchAdminAvatars: async (skip = 0, search: string | undefined = undefined, sortField = 'name', orderBy = 'asc') => {
const sortData = {}
if (sortField.length > 0) {
sortData[sortField] = orderBy === 'desc' ? 0 : 1
sortData[sortField] = orderBy === 'desc' ? -1 : 1
}
const adminAvatarState = getMutableState(AdminAvatarState)
const limit = adminAvatarState.limit.value
const avatars = (await API.instance.client.service('avatar').find({
const avatars = (await API.instance.client.service(avatarPath).find({
query: {
admin: true,
$sort: {
Expand All @@ -109,12 +108,12 @@ export const AdminAvatarService = {
$skip: skip * AVATAR_PAGE_LIMIT,
search: search
}
})) as Paginated<AvatarInterface>
})) as Paginated<AvatarType>
dispatchAction(AdminAvatarActions.avatarsFetched({ avatars }))
},
removeAdminAvatar: async (id: string) => {
try {
await API.instance.client.service('avatar').remove(id)
await API.instance.client.service(avatarPath).remove(id)
dispatchAction(AdminAvatarActions.avatarRemoved({}))
} catch (err) {
logger.error(err)
Expand All @@ -126,7 +125,7 @@ export const AdminAvatarService = {
export class AdminAvatarActions {
static avatarsFetched = defineAction({
type: 'ee.client.AdminAvatar.AVATARS_RETRIEVED' as const,
avatars: matches.object as Validator<unknown, AvatarResult>
avatars: matches.object as Validator<unknown, Paginated<AvatarType>>
})

static avatarCreated = defineAction({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ import { createState, useHookstate } from '@hookstate/core'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AvatarEffectComponent } from '@etherealengine/engine/src/avatar/components/AvatarEffectComponent'
import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { hasComponent } from '@etherealengine/engine/src/ecs/functions/ComponentFunctions'
import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { createXRUI } from '@etherealengine/engine/src/xrui/functions/createXRUI'
import { WidgetAppService } from '@etherealengine/engine/src/xrui/WidgetAppService'
import { WidgetName } from '@etherealengine/engine/src/xrui/Widgets'
Expand Down Expand Up @@ -62,7 +62,7 @@ const SelectAvatarMenu = () => {

const [page, setPage] = useState(0)
const [imgPerPage, setImgPerPage] = useState(Math.min(getAvatarPerPage(), avatarState.total.value))
const [selectedAvatar, setSelectedAvatar] = useState<any>('')
const [selectedAvatar, setSelectedAvatar] = useState<AvatarType | undefined>(undefined)

useEffect(() => {
AvatarService.fetchAvatarList()
Expand Down Expand Up @@ -93,14 +93,14 @@ const SelectAvatarMenu = () => {
}

const confirmAvatar = () => {
if (selectedAvatar && avatarId != selectedAvatar?.avatar?.name) {
if (selectedAvatar && avatarId != selectedAvatar?.name) {
setAvatar(selectedAvatar?.id || '')
WidgetAppService.setWidgetVisibility(WidgetName.PROFILE, false)
}
setSelectedAvatar('')
setSelectedAvatar(undefined)
}

const selectAvatar = (avatarResources: AvatarInterface) => {
const selectAvatar = (avatarResources: AvatarType) => {
setSelectedAvatar(avatarResources)
}

Expand All @@ -125,7 +125,7 @@ const SelectAvatarMenu = () => {
key={avatar.id}
xr-layer="true"
onClick={() => selectAvatar(avatar)}
className={`paperAvatar ${avatar.name == selectedAvatar?.avatar?.name ? 'selectedAvatar' : ''}
className={`paperAvatar ${avatar.name == selectedAvatar?.name ? 'selectedAvatar' : ''}
${avatar.name == avatarId ? 'activeAvatar' : ''}`}
style={{
pointerEvents: avatar.name == avatarId ? 'none' : 'auto',
Expand Down Expand Up @@ -175,7 +175,7 @@ const SelectAvatarMenu = () => {
xr-layer="true"
backgroundColor="#f87678"
onClick={() => {
setSelectedAvatar('')
setSelectedAvatar(undefined)
}}
disabled={!selectedAvatar}
content={<span style={{ fontSize: '15px', fontWeight: 'bold' }}>X</span>}
Expand All @@ -184,7 +184,7 @@ const SelectAvatarMenu = () => {
xr-layer="true"
backgroundColor="#23af3a"
onClick={confirmAvatar}
disabled={selectedAvatar?.avatar?.name == avatarId}
disabled={selectedAvatar?.name == avatarId}
content={<Icon type="Check" />}
/>
<XRIconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ import {
THUMBNAIL_HEIGHT,
THUMBNAIL_WIDTH
} from '@etherealengine/common/src/constants/AvatarConstants'
import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader'
import { AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import Box from '@etherealengine/ui/src/primitives/mui/Box'
import CircularProgress from '@etherealengine/ui/src/primitives/mui/CircularProgress'
import Grid from '@etherealengine/ui/src/primitives/mui/Grid'
Expand All @@ -59,7 +59,7 @@ import styles from '../index.module.scss'
import { PopupMenuServices } from '../PopupMenuService'

interface Props {
selectedAvatar?: AvatarInterface
selectedAvatar?: AvatarType
}

const defaultState = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { UserId } from '@etherealengine/common/src/interfaces/UserId'
import { AvatarState } from '@etherealengine/engine/src/avatar/state/AvatarNetworkState'
import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { WorldState } from '@etherealengine/engine/src/networking/interfaces/WorldState'
import { avatarPath } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux'

import { AuthState } from '../services/AuthService'
Expand All @@ -45,7 +46,7 @@ export const useUserAvatarThumbnail = (userID = '' as UserId) => {
useEffect(() => {
if (!userAvatarState.avatarID?.value) return
Engine.instance.api
.service('avatar')
.service(avatarPath)
.get(userAvatarState.avatarID.value)
.then((avatarDetails) => {
avatarState.set(avatarDetails.thumbnailResource?.url ?? DEFAULT_PROFILE_IMG_PLACEHOLDER)
Expand Down
53 changes: 12 additions & 41 deletions packages/client-core/src/user/services/AvatarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,16 @@ Ethereal Engine. All Rights Reserved.
*/

import { Paginated } from '@feathersjs/feathers'
import axios from 'axios'
import i18n from 'i18next'

import config from '@etherealengine/common/src/config'
import { AvatarInterface } from '@etherealengine/common/src/interfaces/AvatarInterface'
import { EntityUUID } from '@etherealengine/common/src/interfaces/EntityUUID'
import { StaticResourceInterface } from '@etherealengine/common/src/interfaces/StaticResourceInterface'
import { UserId } from '@etherealengine/common/src/interfaces/UserId'
import { AvatarNetworkAction } from '@etherealengine/engine/src/avatar/state/AvatarNetworkState'
import { matches, Validator } from '@etherealengine/engine/src/common/functions/MatchesUtils'
import { Engine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { WorldNetworkAction } from '@etherealengine/engine/src/networking/functions/WorldNetworkAction'
import { avatarPath, AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema'
import { defineAction, defineState, dispatchAction, getMutableState, getState } from '@etherealengine/hyperflux'

import { NotificationService } from '../../common/services/NotificationService'
import { uploadToFeathersService } from '../../util/upload'
import { AuthAction, AuthState } from './AuthService'

Expand All @@ -48,7 +43,7 @@ export const AVATAR_PAGE_LIMIT = 100
export const AvatarState = defineState({
name: 'AvatarState',
initial: () => ({
avatarList: [] as Array<AvatarInterface>,
avatarList: [] as Array<AvatarType>,
search: undefined as string | undefined,
skip: 0,
limit: AVATAR_PAGE_LIMIT,
Expand All @@ -73,7 +68,7 @@ export const AvatarServiceReceptor = (action) => {

export const AvatarService = {
async createAvatar(model: File, thumbnail: File, avatarName: string, isPublic: boolean) {
const newAvatar = await Engine.instance.api.service('avatar').create({
const newAvatar = await Engine.instance.api.service(avatarPath).create({
name: avatarName,
isPublic
})
Expand All @@ -91,20 +86,20 @@ export const AvatarService = {
const skip = getState(AvatarState).skip
const newSkip =
incDec === 'increment' ? skip + AVATAR_PAGE_LIMIT : incDec === 'decrement' ? skip - AVATAR_PAGE_LIMIT : skip
const result = (await Engine.instance.api.service('avatar').find({
const result = (await Engine.instance.api.service(avatarPath).find({
query: {
search,
$skip: newSkip,
$limit: AVATAR_PAGE_LIMIT
}
})) as Paginated<AvatarInterface>
})) as Paginated<AvatarType>
dispatchAction(
AvatarActions.updateAvatarListAction({ avatarList: result.data, search, skip: result.skip, total: result.total })
)
},

async patchAvatar(
originalAvatar: AvatarInterface,
originalAvatar: AvatarType,
avatarName: string,
updateModels: boolean,
avatarFile?: File,
Expand All @@ -124,7 +119,8 @@ export const AvatarService = {
originalAvatar.isPublic,
originalAvatar.id
)
const removalPromises = [] as any

const removalPromises: Promise<StaticResourceInterface>[] = []
if (uploadResponse[0].id !== originalAvatar.modelResourceId)
removalPromises.push(AvatarService.removeStaticResource(originalAvatar.modelResourceId))
if (uploadResponse[1].id !== originalAvatar.thumbnailResourceId)
Expand All @@ -138,7 +134,7 @@ export const AvatarService = {
}
}

const avatar = await Engine.instance.api.service('avatar').patch(originalAvatar.id, payload)
const avatar = await Engine.instance.api.service(avatarPath).patch(originalAvatar.id, payload)
dispatchAction(AvatarActions.updateAvatarAction({ avatar }))

const authState = getState(AuthState)
Expand All @@ -149,12 +145,6 @@ export const AvatarService = {
}
},

async removeAvatar(keys: string) {
await Engine.instance.api.service('avatar').remove('', { query: { keys } })
NotificationService.dispatchNotify(i18n.t('user:avatar.remove-success-msg'), { variant: 'success' })
return this.fetchAvatarList()
},

async removeStaticResource(id: string) {
return Engine.instance.api.service('static-resource').remove(id)
},
Expand All @@ -171,25 +161,6 @@ export const AvatarService = {
)
},

async uploadAvatar(data: any) {
const authState = getState(AuthState)
const token = authState.authUser.accessToken
const selfUser = authState.user
const res = await axios.post(`https://${config.client.serverHost}/upload`, data, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + token
}
})
const userId = selfUser.id ?? null
await Engine.instance.api.service('user').patch(userId, {
name: selfUser.name
})
const result = res.data
NotificationService.dispatchNotify('Avatar updated', { variant: 'success' })
dispatchAction(AuthAction.avatarUpdatedAction({ url: result.url }))
},

async uploadAvatarModel(avatar: File, thumbnail: File, avatarName: string, isPublic: boolean, avatarId?: string) {
return uploadToFeathersService('upload-asset', [avatar, thumbnail], {
type: 'user-avatar-upload',
Expand All @@ -203,7 +174,7 @@ export const AvatarService = {

async getAvatar(id: string) {
try {
return Engine.instance.api.service('avatar').get(id)
return Engine.instance.api.service(avatarPath).get(id)
} catch (err) {
return null
}
Expand All @@ -213,13 +184,13 @@ export const AvatarService = {
export class AvatarActions {
static updateAvatarListAction = defineAction({
type: 'ee.client.avatar.AVATAR_FETCHED' as const,
avatarList: matches.array as Validator<unknown, AvatarInterface[]>,
avatarList: matches.array as Validator<unknown, AvatarType[]>,
search: matches.string.optional(),
skip: matches.number,
total: matches.number
})
static updateAvatarAction = defineAction({
type: 'ee.client.avatar.AVATAR_UPDATED' as const,
avatar: matches.object as Validator<unknown, AvatarInterface>
avatar: matches.object as Validator<unknown, AvatarType>
})
}
Loading

0 comments on commit 28dd289

Please sign in to comment.