**写在前面**
该系列文章是为具有开发能力的朋友写作的,目的是帮助他们在刮擦3.0的基础上开发一套完整的集刮擦3.0编程工具,用户社区和作品云端存储及分享,品牌集成于一体的刮擦编程平台。如果您不是开发者,但想要拥有自己的教育平台和品牌,也欢迎学习交流和洽谈合作。
所以如果您是想学习scratch少儿编程课程,那请忽略该系列的文章。
**前言**
前面我们将scratch-gui工程成功建造运行起来,并且成功植入了我们的品牌徽标,这让我们对整个项目有了初步的认识。
现在我们已经有了scratch编程工具,但是我们还剩下两个主要的后台,用户社区后台和GUI的存储后台。目前Scratch3.0团队还没有发现社区替换和GUI的存储部分是否有开源计划,考虑到Scratch2.0的重新初始化开源,3.0社区初始化开源的可能也不大。
scratch-www项目提供了用户社区的功能,但是需要通过接口去分析它的后台的数据存储的结构,我觉得比较麻烦,不如我们自己来开发一个,集成到我们的编程工具scratch-gui中。
所以接下来我们的工作是自己来提供相关的两个前提平台,并与GUI集成到一起。
**约会用户登录状态**
我们先一步一步来,先做一个比较简单的用户系统,再一步一步迭代。
这一章,我们先来改造一下前面的scratch-gui,约会用户登录状态的检测。
在进入项目时,检测用户是否登录,如果用户未登录,则在右上角显示登录按钮,否则显示用户头像和姓名等基本信息。
先在减少器目录中创建user-state.js文件,用作记录用户的信息。
添加如下内容:
> import keyMirror from 'keymirror';
>
> const UserState = keyMirror({
>
> NOT\_LOGINED: null,
>
> LOGINED: null
>
> });
>
> const UserStates = Object.keys(UserState)
>
> const initialState = {
>
> error: null,
>
> userData: null,
>
> loginState: UserState.NOT\_LOGINED
>
> };
>
> const getIsLogined = loginState => (
>
> loginState === UserState.LOGINED
>
> );
>
> const reducer = function (state, action) {
>
> if (typeof state === 'undefined') state = initialState;
>
> }
在减少器/gui.js中,作为项目的用户相关的初始化信息。
在reducer / gui.js中日期用户状态:
>
>
> ~~~
> import userStateReducer, {userStateInitialState} from './user-state';
> ```
> 加入到initialState中:
> ```
> const guiInitialState = {
> alerts: alertsInitialState,
> assetDrag: assetDragInitialState,
> blockDrag: blockDragInitialState,
> cards: cardsInitialState,
> colorPicker: colorPickerInitialState,
> connectionModal: connectionModalInitialState,
> customProcedures: customProceduresInitialState,
> editorTab: editorTabInitialState,
> mode: modeInitialState,
> hoveredTarget: hoveredTargetInitialState,
> stageSize: stageSizeInitialState,
> menus: menuInitialState,
> micIndicator: micIndicatorInitialState,
> modals: modalsInitialState,
> monitors: monitorsInitialState,
> monitorLayout: monitorLayoutInitialState,
> projectChanged: projectChangedInitialState,
> projectState: projectStateInitialState,
> projectTitle: projectTitleInitialState,
> fontsLoaded: fontsLoadedInitialState,
> restoreDeletion: restoreDeletionInitialState,
> targets: targetsInitialState,
> timeout: timeoutInitialState,
> toolbox: toolboxInitialState,
> vm: vmInitialState,
> vmStatus: vmStatusInitialState,
> userState: userStateInitialState
> };
> ```
> 将reducer加入到guiReducer中:
> ```
> const guiReducer = combineReducers({
> alerts: alertsReducer,
> assetDrag: assetDragReducer,
> blockDrag: blockDragReducer,
> cards: cardsReducer,
> colorPicker: colorPickerReducer,
> connectionModal: connectionModalReducer,
> customProcedures: customProceduresReducer,
> editorTab: editorTabReducer,
> mode: modeReducer,
> hoveredTarget: hoveredTargetReducer,
> stageSize: stageSizeReducer,
> menus: menuReducer,
> micIndicator: micIndicatorReducer,
> modals: modalReducer,
> monitors: monitorReducer,
> monitorLayout: monitorLayoutReducer,
> projectChanged: projectChangedReducer,
> projectState: projectStateReducer,
> projectTitle: projectTitleReducer,
> fontsLoaded: fontsLoadedReducer,
> restoreDeletion: restoreDeletionReducer,
> targets: targetReducer,
> timeout: timeoutReducer,
> toolbox: toolboxReducer,
> vm: vmReducer,
> vmStatus: vmStatusReducer,
> userState: userStateReducer
> });
> ~~~
>
>
下面去container / gui.jsx中为里面定义的GUI Component添加loginState这个道具,使用标识用户是否登录:
>
>
> ~~~
> GUI.propTypes = {
> assetHost: PropTypes.string,
> children: PropTypes.node,
> cloudHost: PropTypes.string,
> error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
> fetchingProject: PropTypes.bool,
> intl: intlShape,
> isError: PropTypes.bool,
> isLoading: PropTypes.bool,
> isScratchDesktop: PropTypes.bool,
> isShowingProject: PropTypes.bool,
> loadingStateVisible: PropTypes.bool,
> onProjectLoaded: PropTypes.func,
> onSeeCommunity: PropTypes.func,
> onStorageInit: PropTypes.func,
> onUpdateProjectId: PropTypes.func,
> onUpdateProjectTitle: PropTypes.func,
> onUpdateReduxProjectTitle: PropTypes.func,
> onVmInit: PropTypes.func,
> projectHost: PropTypes.string,
> projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
> projectTitle: PropTypes.string,
> telemetryModalVisible: PropTypes.bool,
> vm: PropTypes.instanceOf(VM).isRequired,
> loginState: PropTypes.bool
> };
> ~~~
>
>
这个`loginState`道具的状态值来自于user-state.js中getIsLogined中检测当前的loginState(指状态中的)是否等于UserState.LOGINED:
>
>
> ~~~
> const mapStateToProps = state => {
> const loadingState = state.scratchGui.projectState.loadingState;
> const loginState = state.scratchGui.userState.loginState;
> return {
> activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
> alertsVisible: state.scratchGui.alerts.visible,
> backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
> blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
> cardsVisible: state.scratchGui.cards.visible,
> connectionModalVisible: state.scratchGui.modals.connectionModal,
> costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
> costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
> error: state.scratchGui.projectState.error,
> isError: getIsError(loadingState),
> isFullScreen: state.scratchGui.mode.isFullScreen,
> isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
> isRtl: state.locales.isRtl,
> isShowingProject: getIsShowingProject(loadingState),
> loadingStateVisible: state.scratchGui.modals.loadingProject,
> projectId: state.scratchGui.projectState.projectId,
> soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX,
> targetIsStage: (
> state.scratchGui.targets.stage &&
> state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
> ),
> telemetryModalVisible: state.scratchGui.modals.telemetryModal,
> tipsLibraryVisible: state.scratchGui.modals.tipsLibrary,
> vm: state.scratchGui.vm,
> loginState: getIsLogined(loginState)
> };
> };
> ~~~
>
>
现在container / gui.jsx中定义的Component GUI具有登录状态属性了,我们要把它传到menu-bar中,因为我们要在menu-bar中去控制右上角的显示状态。
在这个GUI组件中使用了components / gui / gui.jsx定义的GUIComponent这个组件,GUIComponent定义了整个项目的基本样式结构中,可以找到对MenuBar的使用。
首先,在GUIComponent的定义中日期之前定义的`loginState`:
>
>
> ~~~
> const GUIComponent = props => {
> const {
> accountNavOpen,
> activeTabIndex,
> alertsVisible,
> authorId,
> authorThumbnailUrl,
> authorUsername,
> basePath,
> backdropLibraryVisible,
> backpackHost,
> backpackVisible,
> blocksTabVisible,
> cardsVisible,
> canCreateNew,
> canEditTitle,
> canRemix,
> canSave,
> canCreateCopy,
> canShare,
> canUseCloud,
> children,
> connectionModalVisible,
> costumeLibraryVisible,
> costumesTabVisible,
> enableCommunity,
> intl,
> isCreating,
> isFullScreen,
> isPlayerOnly,
> isRtl,
> isShared,
> loading,
> renderLogin,
> onClickAccountNav,
> onCloseAccountNav,
> onLogOut,
> onOpenRegistration,
> onToggleLoginOpen,
> onUpdateProjectTitle,
> onActivateCostumesTab,
> onActivateSoundsTab,
> onActivateTab,
> onClickLogo,
> onExtensionButtonClick,
> onProjectTelemetryEvent,
> onRequestCloseBackdropLibrary,
> onRequestCloseCostumeLibrary,
> onRequestCloseTelemetryModal,
> onSeeCommunity,
> onShare,
> onTelemetryModalCancel,
> onTelemetryModalOptIn,
> onTelemetryModalOptOut,
> showComingSoon,
> soundsTabVisible,
> stageSizeMode,
> targetIsStage,
> telemetryModalVisible,
> tipsLibraryVisible,
> vm,
> loginState,
> ...componentProps
> } = omit(props, 'dispatch');
> ...
> ~~~
>
>
再在使用MenuBar的地方也为MenuBar定义`loginState`属性,它的值就是GUIComponent传进来的`loginState`的值:
>
>
>
最后修改components / menu-bar.jsx中的MenuBar组件的显示,将右上角替换成:
> {this.props.loginState ? (
>
> className={classNames(
>
> styles.menuBarItem,
>
> styles.hoverable,
>
> styles.mystuffButton
>
> )}
>
> >
>
> className={styles.mystuffIcon}
>
> src={mystuffIcon}
>
> />
>
> id="account-nav"
>
> place={this.props.isRtl ? 'right' : 'left'}
>
> >
>
> className={classNames(
>
> styles.menuBarItem,
>
> styles.hoverable,
>
> styles.accountNavMenu
>
> )}
>
> >
>
> className={styles.profileIcon}
>
> src={profileIcon}
>
> />
>
> {'scratch-cat'}
>
> className={styles.dropdownCaretIcon}
>
> src={dropdownCaret}
>
> />
>
> ) : Login}
如果用户已登录,就显示头像和姓名的样式(具体的用户信息需要跟后台打通,我们后面再实现):
![](https://img-blog.csdnimg.cn/20200127181021798.png)
否则显示登录按钮:
![](https://img-blog.csdnimg.cn/20200127181041268.png)
我们可以通过修改reducers / user-state.js中的loginState的初始值来查看效果:
> loginState: UserState.NOT\_LOGINED
> loginState: UserState.LOGINED
这个值我们会在后面根据用户登录的token去获取。
为了与项目整体风格一致,我们修改这个登录按钮的样式,在菜单栏目录中添加login-button.css和login-button.jsx文件,内容分别如下:
>
>
> ~~~
> @import "../../css/colors.css";
>
> .login-button { background: $data-primary;}
> ~~~
>
>
> import classNames from 'classnames';
>
> import {FormattedMessage} from 'react-intl';
>
> import PropTypes from 'prop-types';
>
> import React from 'react';
>
> import Button from '../button/button.jsx';
>
> import styles from './login-button.css';
>
> const LoginButton = ({
>
> className,
>
> onClick
>
> }) => (
>
> className={classNames(
>
> className,
>
> styles.loginButton
>
> )}
>
> onClick={onClick}
>
> >
>
> defaultMessage="Login"
>
> description="Label for login"
>
> id="gui.menuBar.login"
>
> />
>
> );
>
> LoginButton.propTypes = {
>
> className: PropTypes.string,
>
> onClick: PropTypes.func
>
> };
>
> LoginButton.defaultProps = {
>
> onClick: () => {}
>
> };
>
> export default LoginButton;
然后在menu-bar.jsx中如下使用:
>
这样看起来就好看多了:
![](https://img-blog.csdnimg.cn/20200127181254620.png)
好了,这里接收完成后,我们接下来就可以实现一个后台系统,然后对接后台系统登录和获取用户信息了。