Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for avatar service to use schema, knex & feathers 5 #8261

Merged
merged 9 commits into from
Jul 19, 2023
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