From e28d5575fdc76662814058094324398aec8c4cde Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Fri, 14 Nov 2025 15:10:08 +0200 Subject: [PATCH 1/3] feat: add the ability to display avatar badge for users with active membership --- .../scratch-gui/src/components/gui/gui.jsx | 3 +++ .../src/components/menu-bar/account-nav.jsx | 5 +++- .../src/components/menu-bar/author-info.jsx | 7 ++++-- .../src/components/menu-bar/cat-ears.svg | 3 +++ .../src/components/menu-bar/menu-bar.jsx | 5 ++++ .../src/components/menu-bar/user-avatar.css | 25 +++++++++++++++++++ .../src/components/menu-bar/user-avatar.jsx | 25 +++++++++++-------- packages/scratch-gui/src/css/colors.css | 1 + 8 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 packages/scratch-gui/src/components/menu-bar/cat-ears.svg diff --git a/packages/scratch-gui/src/components/gui/gui.jsx b/packages/scratch-gui/src/components/gui/gui.jsx index 1fa06119e2..ba11a94696 100644 --- a/packages/scratch-gui/src/components/gui/gui.jsx +++ b/packages/scratch-gui/src/components/gui/gui.jsx @@ -59,6 +59,7 @@ const GUIComponent = props => { authorId, authorThumbnailUrl, authorUsername, + authorAvatarBadge, basePath, backdropLibraryVisible, backpackHost, @@ -257,6 +258,7 @@ const GUIComponent = props => { authorId={authorId} authorThumbnailUrl={authorThumbnailUrl} authorUsername={authorUsername} + authorAvatarBadge={authorAvatarBadge} canChangeLanguage={canChangeLanguage} canChangeTheme={canChangeTheme} canCreateCopy={canCreateCopy} @@ -435,6 +437,7 @@ GUIComponent.propTypes = { authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false authorThumbnailUrl: PropTypes.string, authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false + authorAvatarBadge: PropTypes.number, backdropLibraryVisible: PropTypes.bool, backpackHost: PropTypes.string, backpackVisible: PropTypes.bool, diff --git a/packages/scratch-gui/src/components/menu-bar/account-nav.jsx b/packages/scratch-gui/src/components/menu-bar/account-nav.jsx index 2cb7d37e49..eb79d5d4a3 100644 --- a/packages/scratch-gui/src/components/menu-bar/account-nav.jsx +++ b/packages/scratch-gui/src/components/menu-bar/account-nav.jsx @@ -31,7 +31,8 @@ const AccountNavComponent = ({ myClassesUrl, myClassUrl, accountSettingsUrl, - username + username, + avatarBadge }) => (
) : null} @@ -143,6 +145,7 @@ AccountNavComponent.propTypes = { onLogOut: PropTypes.func, username: PropTypes.string, + avatarBadge: PropTypes.number, avatarUrl: PropTypes.string, myStuffUrl: PropTypes.string, diff --git a/packages/scratch-gui/src/components/menu-bar/author-info.jsx b/packages/scratch-gui/src/components/menu-bar/author-info.jsx index 50379d6263..4c769297be 100644 --- a/packages/scratch-gui/src/components/menu-bar/author-info.jsx +++ b/packages/scratch-gui/src/components/menu-bar/author-info.jsx @@ -12,7 +12,8 @@ const AuthorInfo = ({ projectTitle, // TODO: use userId to link to user's profile userId, - username + username, + avatarBadge }) => (
@@ -54,7 +56,8 @@ AuthorInfo.propTypes = { imageUrl: PropTypes.string, projectTitle: PropTypes.string, userId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), - username: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]) + username: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + avatarBadge: PropTypes.number }; export default AuthorInfo; diff --git a/packages/scratch-gui/src/components/menu-bar/cat-ears.svg b/packages/scratch-gui/src/components/menu-bar/cat-ears.svg new file mode 100644 index 0000000000..046de35b07 --- /dev/null +++ b/packages/scratch-gui/src/components/menu-bar/cat-ears.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index 58f955a1bb..e77e411463 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -648,6 +648,7 @@ class MenuBar extends React.Component { projectTitle={this.props.projectTitle} userId={this.props.authorId} username={this.props.authorUsername} + avatarBadge={this.props.authorAvatarBadge} /> ) : null)}
@@ -782,6 +783,7 @@ class MenuBar extends React.Component { onLogOut={menuOpts.canLogout ? this.props.onLogOut : null} username={this.props.username} + avatarBadge={this.props.avatarBadge} avatarUrl={menuOpts.avatarUrl} myStuffUrl={menuOpts.myStuffUrl} @@ -899,6 +901,7 @@ MenuBar.propTypes = { authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), authorThumbnailUrl: PropTypes.string, authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + authorAvatarBadge: PropTypes.number, autoUpdateProject: PropTypes.func, canChangeLanguage: PropTypes.bool, canChangeTheme: PropTypes.bool, @@ -975,6 +978,7 @@ MenuBar.propTypes = { shouldSaveBeforeTransition: PropTypes.func, showComingSoon: PropTypes.bool, username: PropTypes.string, + avatarBadge: PropTypes.number, userOwnsProject: PropTypes.bool, accountMenuOptions: AccountMenuOptionsPropTypes, @@ -1008,6 +1012,7 @@ const mapStateToProps = (state, ownProps) => { projectTitle: state.scratchGui.projectTitle, settingsMenuOpen: settingsMenuOpen(state), username: ownProps.username ?? (user ? user.username : null), + avatarBadge: ownProps.avatarBadge ?? (user ? user.membership_avatar_badge : null), userIsEducator: permissions && permissions.educator, vm: state.scratchGui.vm, mode220022BC: isTimeTravel220022BC(state), diff --git a/packages/scratch-gui/src/components/menu-bar/user-avatar.css b/packages/scratch-gui/src/components/menu-bar/user-avatar.css index 2d1ac6fefd..7caef71fae 100644 --- a/packages/scratch-gui/src/components/menu-bar/user-avatar.css +++ b/packages/scratch-gui/src/components/menu-bar/user-avatar.css @@ -8,3 +8,28 @@ vertical-align: middle; box-shadow: 0 0 0 1px $ui-black-transparent; } + +.avatar-badge { + border: 1px solid $ui-orange; + box-sizing: border-box; + border-radius: 17%; + box-shadow: none; +} + +.avatar-badge-wrapper { + display: inline-block; + background: url('./cat-ears.svg') no-repeat top ; + background-size: contain; + align-content: end; + + width: $menu-bar-button-size; + height: calc($menu-bar-button-size * 1.28); +} + +[dir="ltr"] .avatar-badge-wrapper { + margin-right: calc($space * .8125); +} + +[dir="rtl"] .avatar-badge-wrapper { + margin-left: calc($space * .8125); +} diff --git a/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx b/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx index df0f042afa..5df3597ec3 100644 --- a/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx @@ -6,21 +6,26 @@ import styles from './user-avatar.css'; const UserAvatar = ({ className, - imageUrl + imageUrl, + showAvatarBadge = false }) => ( - +
+ +
); UserAvatar.propTypes = { className: PropTypes.string, - imageUrl: PropTypes.string + imageUrl: PropTypes.string, + showAvatarBadge: PropTypes.bool, }; export default UserAvatar; diff --git a/packages/scratch-gui/src/css/colors.css b/packages/scratch-gui/src/css/colors.css index 538d4e8d03..f01a3f6909 100644 --- a/packages/scratch-gui/src/css/colors.css +++ b/packages/scratch-gui/src/css/colors.css @@ -15,6 +15,7 @@ $ui-black-transparent-10: hsla(0, 0%, 0%, 0.10); /* 10% transparent version of b $ui-green: hsla(163, 85%, 35%, 1); /* #0DA57A */ $ui-green-2: hsla(163, 85%, 40%, 1); /* #0FBD8C */ +$ui-orange: hsla(37, 96%, 55%, 1); /* #FAA51D */ $text-primary: hsla(225, 15%, 40%, 1); /* #575E75 */ $text-primary-transparent: hsla(225, 15%, 40%, 0.75); From 2fbf7352c69b1d69ac0286914bf0b1cf2f48eee1 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Fri, 14 Nov 2025 15:36:42 +0200 Subject: [PATCH 2/3] fix: remove trailing comma --- packages/scratch-gui/src/components/menu-bar/user-avatar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx b/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx index 5df3597ec3..f6e05510c9 100644 --- a/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/user-avatar.jsx @@ -25,7 +25,7 @@ const UserAvatar = ({ UserAvatar.propTypes = { className: PropTypes.string, imageUrl: PropTypes.string, - showAvatarBadge: PropTypes.bool, + showAvatarBadge: PropTypes.bool }; export default UserAvatar; From e57442102a579cc3e5043c3ef50cf0da5182f638 Mon Sep 17 00:00:00 2001 From: Ayshe Dzhindzhi Date: Tue, 18 Nov 2025 13:26:30 +0200 Subject: [PATCH 3/3] fix: always get avatar_badge from the session --- packages/scratch-gui/src/components/menu-bar/menu-bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx index e77e411463..ff83c645cf 100644 --- a/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx +++ b/packages/scratch-gui/src/components/menu-bar/menu-bar.jsx @@ -1012,7 +1012,7 @@ const mapStateToProps = (state, ownProps) => { projectTitle: state.scratchGui.projectTitle, settingsMenuOpen: settingsMenuOpen(state), username: ownProps.username ?? (user ? user.username : null), - avatarBadge: ownProps.avatarBadge ?? (user ? user.membership_avatar_badge : null), + avatarBadge: user ? user.membership_avatar_badge : null, userIsEducator: permissions && permissions.educator, vm: state.scratchGui.vm, mode220022BC: isTimeTravel220022BC(state),