From 7501e3ebe799cb0471d9b2e66df7e5089cba199b Mon Sep 17 00:00:00 2001 From: Vladislav Frolov <50615459+Cheshiriks@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:40:13 +0300 Subject: [PATCH] Added default avatar (#2947) * Added default avatar --- .../components/topbar/TopBarUserField.kt | 17 +++-- .../components/basic/AvatarRenderers.kt | 67 ++++++++++++++++++- .../views/userprofile/UserProfileView.kt | 9 ++- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/cosv-frontend/src/main/kotlin/com/saveourtool/cosv/frontend/components/topbar/TopBarUserField.kt b/cosv-frontend/src/main/kotlin/com/saveourtool/cosv/frontend/components/topbar/TopBarUserField.kt index c0e570ef41..03d71754ff 100644 --- a/cosv-frontend/src/main/kotlin/com/saveourtool/cosv/frontend/components/topbar/TopBarUserField.kt +++ b/cosv-frontend/src/main/kotlin/com/saveourtool/cosv/frontend/components/topbar/TopBarUserField.kt @@ -5,6 +5,7 @@ package com.saveourtool.cosv.frontend.components.topbar import com.saveourtool.frontend.common.components.basic.avatarRenderer +import com.saveourtool.frontend.common.components.basic.renderTopBarAvatar import com.saveourtool.frontend.common.components.modal.logoutModal import com.saveourtool.frontend.common.externals.fontawesome.* import com.saveourtool.frontend.common.externals.i18next.useTranslation @@ -19,7 +20,6 @@ import react.* import react.dom.aria.* import react.dom.html.ReactHTML.a import react.dom.html.ReactHTML.div -import react.dom.html.ReactHTML.img import react.dom.html.ReactHTML.li import react.dom.html.ReactHTML.small import react.dom.html.ReactHTML.span @@ -48,6 +48,7 @@ val topBarUserField: FC = FC { props -> val navigate = useNavigate() var isLogoutModalOpen by useState(false) var isAriaExpanded by useState(false) + var (isError, setIsError) = useState(false) ul { className = ClassName("navbar-nav ml-auto") @@ -86,12 +87,14 @@ val topBarUserField: FC = FC { props -> } } } - props.userInfo?.let { userInfo -> - img { - className = - ClassName("ml-2 align-self-center avatar avatar-user width-full border color-bg-default rounded-circle fas mr-2") - src = props.userInfo?.avatar?.avatarRenderer() ?: AVATAR_PROFILE_PLACEHOLDER - style = logoSize + props.userInfo?.let { it -> + renderTopBarAvatar( + it.avatar?.avatarRenderer() ?: AVATAR_PROFILE_PLACEHOLDER, + "ml-2 align-self-center width-full fas mr-2", + logoSize, + isError, + ) { + setIsError(true) } } ?: fontAwesomeIcon(icon = faUser) { it.className = "m-2 align-self-center fas fa-lg fa-fw mr-2 text-gray-400" diff --git a/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/basic/AvatarRenderers.kt b/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/basic/AvatarRenderers.kt index daba8d97d4..574987fb11 100644 --- a/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/basic/AvatarRenderers.kt +++ b/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/basic/AvatarRenderers.kt @@ -18,6 +18,7 @@ import react.ChildrenBuilder import react.dom.html.ReactHTML.div import react.dom.html.ReactHTML.img import react.router.dom.Link +import react.useState import web.cssom.ClassName import web.cssom.rem @@ -198,17 +199,81 @@ fun ChildrenBuilder.renderOrganizationWithName( } } +/** + * Render organization avatar or placeholder + * + * @param organizationDto organization to render avatar + * @param styleBuilder [CSSProperties] builder + * @param errorAvatars + * @param setErrorAvatars + */ +fun ChildrenBuilder.renderOrganizationAvatar( + organizationDto: OrganizationDto, + errorAvatars: Set, + setErrorAvatars: (String) -> Unit, + styleBuilder: CSSProperties.() -> Unit = {}, +) { + val avatarLink = organizationDto.avatar?.avatarRenderer() ?: AVATAR_ORGANIZATION_PLACEHOLDER + val linkOrganization = "/${organizationDto.name}" + + val renderImg: ChildrenBuilder.() -> Unit = { + img { + className = ClassName("avatar avatar-user border color-bg-default rounded-circle") + src = if (errorAvatars.contains(organizationDto.name)) AVATAR_PROFILE_PLACEHOLDER else avatarLink + style = jso { styleBuilder() } + onError = { + setErrorAvatars(organizationDto.name) + } + } + } + Link { + to = linkOrganization + renderImg() + } +} + +/** + * Render topBar avatar or placeholder + * + * @param avatarLink link to avatar + * @param classes + * @param styleBuilder [CSSProperties] builder + * @param isError + * @param setIsError + */ +fun ChildrenBuilder.renderTopBarAvatar( + avatarLink: String, + classes: String, + styleBuilder: CSSProperties, + isError: Boolean, + setIsError: () -> Unit, +) { + img { + className = ClassName("avatar avatar-user border color-bg-default rounded-circle $classes") + src = if (!isError) avatarLink else AVATAR_PROFILE_PLACEHOLDER + style = styleBuilder + onError = { + setIsError() + } + } +} + private fun ChildrenBuilder.renderAvatar( avatarLink: String, classes: String, link: String?, styleBuilder: CSSProperties.() -> Unit, ) { + val (avatar, setAvatar) = useState(avatarLink) + val renderImg: ChildrenBuilder.() -> Unit = { img { className = ClassName("avatar avatar-user border color-bg-default rounded-circle $classes") - src = avatarLink + src = avatar style = jso { styleBuilder() } + onError = { + setAvatar(AVATAR_PROFILE_PLACEHOLDER) + } } } link?.let { diff --git a/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/views/userprofile/UserProfileView.kt b/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/views/userprofile/UserProfileView.kt index 5dcbfe7387..b5417fba3d 100644 --- a/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/views/userprofile/UserProfileView.kt +++ b/frontend-common/src/main/kotlin/com/saveourtool/frontend/common/components/views/userprofile/UserProfileView.kt @@ -5,6 +5,7 @@ package com.saveourtool.frontend.common.components.views.userprofile import com.saveourtool.frontend.common.components.basic.renderAvatar +import com.saveourtool.frontend.common.components.basic.renderOrganizationAvatar import com.saveourtool.frontend.common.components.inputform.InputTypes import com.saveourtool.frontend.common.components.modal.displayModal import com.saveourtool.frontend.common.components.modal.mediumTransparentModalStyle @@ -47,6 +48,7 @@ val userProfileView: FC = FC { props -> val (organizations, setOrganizations) = useState>(emptyList()) val (selectedMenu, setSelectedMenu) = useState(UserProfileTab.VULNERABILITIES) val (countUsers, setCountUsers) = useState(0) + val (isError, setIsError) = useState(false) useRequest { val userNew: UserInfo = get( @@ -172,6 +174,7 @@ fun ChildrenBuilder.renderLeftUserMenu( ) { val navigate = useNavigate() val banUserWindowOpenness = useWindowOpenness() + val (errorAvatars, setErrorAvatars) = useState(emptySet()) val banUser = useDeferredRequest { user?.name?.let { @@ -369,7 +372,11 @@ fun ChildrenBuilder.renderLeftUserMenu( div { className = ClassName("row") organizations.forEach { organization -> - renderAvatar(organization) { + renderOrganizationAvatar( + organization, + errorAvatars = errorAvatars, + setErrorAvatars = { name -> setErrorAvatars(errorAvatars.plus(name)) } + ) { width = 4.rem height = 4.rem }