diff --git a/.eslintrc b/.eslintrc index 74f9766d204..b78b5466277 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,5 +7,18 @@ "Assets" : false, "chrome" : false, "jscolor" : false - } + }, + "plugins": ["react"], + "rules": { + "jsx-quotes": ["error", "prefer-single"], + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "react/jsx-no-undef": "error", + "react/jsx-fragments": ["error", "syntax"], + }, + "settings": { + "react": { + "version": "detect", + }, + }, } diff --git a/app/setup-wizard/client/final.html b/app/setup-wizard/client/final.html deleted file mode 100644 index 27480e459bc..00000000000 --- a/app/setup-wizard/client/final.html +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/app/setup-wizard/client/index.js b/app/setup-wizard/client/index.js deleted file mode 100644 index 2b99b89e3ed..00000000000 --- a/app/setup-wizard/client/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import './setupWizard.html'; -import './setupWizard'; -import './final.html'; -import './final'; diff --git a/app/setup-wizard/client/setupWizard.html b/app/setup-wizard/client/setupWizard.html deleted file mode 100644 index 9c77129d45e..00000000000 --- a/app/setup-wizard/client/setupWizard.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - diff --git a/app/setup-wizard/server/index.js b/app/setup-wizard/server/index.js deleted file mode 100644 index 43e790a126b..00000000000 --- a/app/setup-wizard/server/index.js +++ /dev/null @@ -1 +0,0 @@ -import './getSetupWizardParameters'; diff --git a/app/theme/client/imports/components/setup-wizard.css b/app/theme/client/imports/components/setup-wizard.css deleted file mode 100644 index 7ab2448c63d..00000000000 --- a/app/theme/client/imports/components/setup-wizard.css +++ /dev/null @@ -1,574 +0,0 @@ -.setup-wizard { - --step-color: var(--rc-color-button-primary); - --highlight-color: var(--rc-color-button-primary); - - display: flex; - - width: 100%; - height: 100%; - - background-color: #f7f8fa; - - justify-content: center; - - &-info { - - overflow: hidden; - flex: 0 1 350px; - - margin: 55px 65px 30px 80px; - - &__header { - display: flex; - - margin: 0 -0.375rem 3rem; - align-items: center; - - &-logo { - height: 1.5rem; - margin: 0 0.375rem; - } - - &-tag { - margin: 0 0.375rem; - - padding: 4px 8px; - - letter-spacing: 0.05rem; - - text-transform: uppercase; - - color: #ffffff; - - border-radius: 50px; - background: #2f343d; - - font-size: 10px; - - line-height: 1rem; - } - } - - &__content { - &-title { - margin-bottom: 1rem; - - letter-spacing: 0.03rem; - - color: #2f343d; - - font-size: 2rem; - - font-weight: 600; - - line-height: 2.6rem; - } - - &-text { - margin-bottom: 3rem; - - color: #9ea2a8; - - font-size: 1rem; - - font-weight: 500; - - line-height: 1.5rem; - } - } - - &__steps { - counter-reset: steps; - - &-item { - position: relative; - - margin: 0 -0.5rem; - - counter-increment: steps; - - color: #d3d5d9; - - font-size: 0.875rem; - font-weight: 500; - - &:not(:last-child) { - margin-bottom: 2rem; - - &::after { - position: absolute; - bottom: -1rem; - left: 1.2rem; - - display: block; - - width: 1px; - height: 1rem; - - content: ""; - - background-color: #d3d5d9; - } - } - - &::before { - display: inline-flex; - - width: 1.5rem; - height: 1.5rem; - margin: 0 0.5rem; - - content: counter(steps); - - color: #d3d5d9; - - border: 1px solid #d3d5d9; - border-radius: 50px; - - font-size: 0.75rem; - align-items: center; - justify-content: center; - } - - &--active { - color: var(--rc-color-button-primary); - - &::before { - color: var(--rc-color-button-primary); - border-color: var(--rc-color-button-primary); - background-color: transparent; - } - } - - &--past { - color: var(--rc-color-primary); - - &::before { - color: var(--rc-color-content); - border-color: var(--rc-color-button-primary); - background-color: var(--rc-color-button-primary); - } - - &::after { - background-color: var(--rc-color-button-primary) !important; - } - - & .setup-wizard-info__steps-item-bonding { - background-color: var(--rc-color-button-primary); - } - } - - &-bonding { - position: absolute; - top: -1rem; - left: 1.2rem; - - display: block; - - width: 1px; - height: 1rem; - - background-color: currentColor; - } - } - } - } - - &-forms { - flex: 1 1 auto; - - &__wrapper { - display: flex; - overflow-y: auto; - - width: calc(100% - 1rem); - height: calc(100% - 2rem); - margin: 1rem 1rem 1rem 0; - - border-radius: 2px; - background: #ffffff; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); - justify-content: center; - } - - &__box { - display: flex; - - visibility: hidden; - flex-direction: column; - - width: 350px; - min-height: min-content; - margin: 3rem; - - transition: opacity 1s linear; - - opacity: 0; - - &--loaded { - visibility: visible; - - opacity: 1; - } - } - - &__header { - margin-bottom: 2rem; - - &-step { - display: block; - - margin-bottom: 3px; - - letter-spacing: 0.05rem; - - text-transform: uppercase; - - color: #caced1; - - font-size: 0.75rem; - - line-height: 1.125rem; - } - - &-title { - letter-spacing: 0.05rem; - - color: #1f2329; - - font-size: 1.25rem; - font-weight: 500; - - line-height: 1.75rem; - } - } - - &__content { - margin-bottom: 2rem; - - &-step { - display: none; - - &--active { - display: block; - } - } - - &-text { - - margin-bottom: 2rem; - - color: #9ea2a8; - - font-size: 1rem; - font-weight: 500; - - line-height: 1.5rem; - } - - &-register { - display: flex; - flex-direction: column; - - &-option { - - display: block; - - padding: 1.5rem; - - cursor: pointer; - - color: #2f343d; - border: 2px solid #e7ebf2; - - border-radius: 2px; - - font-size: 0.875rem; - - line-height: 1.25rem; - - &--selected { - border-color: var(--highlight-color); - } - - &--disabled { - opacity: 0.25; - } - - &:first-child { - margin-bottom: 1rem; - } - } - - &-radio { - position: relative; - - display: flex; - - margin: 0 -0.5rem 1rem; - - &-element { - position: absolute; - z-index: -1; - top: 0; - left: 0; - - width: 0; - height: 0; - - &:checked + .setup-wizard-forms__content-register-radio-fake { - - position: relative; - - border-color: var(--highlight-color); - - &::before { - - position: absolute; - - width: 100%; - height: 100%; - - content: ""; - - border: 2px solid transparent; - border-radius: 50%; - - background-color: var(--highlight-color); - - background-clip: padding-box; - } - } - } - - &-fake { - display: block; - - width: 20px; - height: 20px; - margin: 0 0.5rem; - - border: 2px solid #cfd8e6; - border-radius: 50px; - } - - &-text { - font-weight: 500; - } - } - - &-checkbox { - position: relative; - - display: flex; - - margin: 0 -0.5rem 1rem; - - cursor: inherit; - - &-element { - position: absolute; - z-index: -1; - top: 0; - left: 0; - - width: 0; - height: 0; - - &:checked + .setup-wizard-forms__content-register-checkbox-fake { - position: relative; - - color: var(--rc-color-content); - - border-color: var(--highlight-color); - background-color: var(--highlight-color); - - .setup-wizard-forms__content-register-checkbox-fake-icon { - display: block; - } - } - } - - &-fake { - display: block; - - width: 16px; - height: 16px; - margin: 2px 0.5rem; - - border: 2px solid #cfd8e6; - border-radius: 2px; - - &-icon { - - display: none; - - width: 100%; - height: 100%; - } - } - - &-text { - color: #666666; - } - } - - &-items + * { - margin-top: 1rem; - } - - &-item { - - display: flex; - - margin: 0 -0.5rem 0.5rem; - align-items: center; - - &:last-child { - margin-bottom: 0; - } - - & .setup-wizard-forms__content-register-radio-icon { - width: 20px; - min-width: 20px; - height: 20px; - margin: 0 0.5rem; - align-self: baseline; - - &--check { - color: var(--highlight-color); - } - - &--circle { - height: 6px; - margin: 7px 0.5rem; - } - } - } - } - } - - &__footer { - display: flex; - flex-direction: row; - - margin: 0 -0.5rem 2rem; - - & .rc-button { - margin: 0 0.5rem; - } - } - } - - &-final { - width: 930px; - padding-top: 5rem; - - &__header { - margin-bottom: 5rem; - } - - &__box { - padding: 5rem 6rem; - - border-radius: 2px; - background: #ffffff; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); - - &-title { - margin-bottom: 3.25rem; - } - } - - &__link { - display: block; - - margin-bottom: 1.5rem; - - letter-spacing: 0; - - color: var(--highlight-color); - - font-size: 1rem; - - line-height: 1.5rem; - - &-text { - display: block; - - letter-spacing: 0.03rem; - - text-transform: uppercase; - - color: #2f343d; - - font-size: 0.625rem; - line-height: 1rem; - } - } - } - - & .rc-input { - &:not(:last-child) { - margin-bottom: 1.5rem; - } - - &__title { - letter-spacing: 0.03rem; - - text-transform: uppercase; - - color: #9ea2a8; - - font-size: 0.625rem; - - line-height: 1rem; - } - - &__element { - color: #030c1a; - - font-weight: 500; - } - } - - & .rc-select { - &__element { - color: #030c1a; - - font-size: 0.875rem; - - font-weight: 500; - } - } -} - -.rtl { - & .setup-wizard-info { - margin: 55px 80px 0 65px; - } - - & .setup-wizard-forms__wrapper { - margin: 1rem 0 1rem 1rem; - } - - & .setup-wizard-info__steps-item { - &:not(:last-child)::after, - &-bonding { - right: 1.2rem; - left: auto; - } - } -} - -@media (width <= 760px) { - .setup-wizard { - flex-direction: column; - justify-content: initial; - - & .setup-wizard-forms__wrapper { - width: 100%; - margin: 0; - } - } -} diff --git a/app/theme/client/main.css b/app/theme/client/main.css index d1b828f16b1..d768ec57c47 100644 --- a/app/theme/client/main.css +++ b/app/theme/client/main.css @@ -29,7 +29,6 @@ @import 'imports/components/sidebar/rooms-list.css'; /* Main */ -@import 'imports/components/setup-wizard.css'; @import 'imports/components/flex-nav.css'; @import 'imports/components/header.css'; @import 'imports/components/memberlist.css'; diff --git a/app/ui/client/index.js b/app/ui/client/index.js index 3a134daf54c..80197cff270 100644 --- a/app/ui/client/index.js +++ b/app/ui/client/index.js @@ -8,7 +8,6 @@ import './lib/parentTemplate'; import './lib/codeMirror/codeMirror'; import './lib/textarea-cursor'; import './views/cmsPage.html'; -import './views/fxos.html'; import './views/modal.html'; import './views/404/roomNotFound.html'; import './views/404/invalidSecretURL.html'; @@ -30,7 +29,6 @@ import './views/app/videoCall/videoButtons.html'; import './views/app/videoCall/videoCall.html'; import './views/app/photoswipe.html'; import './views/cmsPage'; -import './views/fxos'; import './views/modal'; import './views/404/roomNotFound'; import './views/app/burger'; diff --git a/app/ui/client/views/fxos.html b/app/ui/client/views/fxos.html deleted file mode 100644 index 3bdbcbf5502..00000000000 --- a/app/ui/client/views/fxos.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/app/ui/client/views/fxos.js b/app/ui/client/views/fxos.js deleted file mode 100644 index 72847ba83a5..00000000000 --- a/app/ui/client/views/fxos.js +++ /dev/null @@ -1,21 +0,0 @@ -import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { Template } from 'meteor/templating'; - -Template.fxOsInstallPrompt.onRendered(function() { - const showPrompt = function() { - const request = window.navigator.mozApps.install(`http://${ location.host }/manifest.webapp`); - request.onsuccess = function() { - BlazeLayout.render('fxOsInstallDone'); - }; - request.onerror = function() { - BlazeLayout.render('fxOsInstallError', { - installError: this.error.name, - }); - }; - }; - setTimeout(showPrompt, 2000); - return $('#initial-page-loading').remove(); -}); - -Template.fxOsInstallDone.onRendered(() => $('#initial-page-loading').remove()); -Template.fxOsInstallError.onRendered(() => $('#initial-page-loading').remove()); diff --git a/client/RocketChat.font.css b/client/RocketChat.font.css new file mode 100644 index 00000000000..7b9074ad89b --- /dev/null +++ b/client/RocketChat.font.css @@ -0,0 +1,14 @@ +@font-face { + font-family: 'RocketChat'; + font-weight: 400; + font-style: normal; + font-display: auto; + + src: url('/fonts/RocketChat.eot'); + src: + url('/fonts/RocketChat.eot?#iefix') format('embedded-opentype'), + url('/fonts/RocketChat.woff2') format('woff2'), + url('/fonts/RocketChat.woff') format('woff'), + url('/fonts/RocketChat.ttf') format('truetype'), + url('/fonts/RocketChat.svg#RocketChat') format('svg'); +} diff --git a/client/components/basic/Button.js b/client/components/basic/Button.js new file mode 100644 index 00000000000..60cd9708a2e --- /dev/null +++ b/client/components/basic/Button.js @@ -0,0 +1,23 @@ +import React from 'react'; + +export const Button = ({ + children, + className, + invisible, + primary, + secondary, + submit, + ...props +}) => ; diff --git a/client/components/basic/Icon.js b/client/components/basic/Icon.js new file mode 100644 index 00000000000..2fc4eb3322c --- /dev/null +++ b/client/components/basic/Icon.js @@ -0,0 +1,13 @@ +import React from 'react'; + +export const Icon = ({ icon, block = '', baseUrl = '', className }) => ; diff --git a/client/components/basic/Input.js b/client/components/basic/Input.js new file mode 100644 index 00000000000..18136a344c2 --- /dev/null +++ b/client/components/basic/Input.js @@ -0,0 +1,67 @@ +import React, { useEffect, useRef } from 'react'; + +import { Icon } from './Icon'; + +export const Input = ({ + error, + title, + icon, + type = 'text', + className, + placeholder, + options, + focused, + ...props +}) => { + const ref = useRef(null); + + useEffect(() => { + if (focused && ref.current) { + ref.current.focus(); + } + }, [focused]); + + return
+ +
; +}; diff --git a/client/components/connectionStatus/ConnectionStatusAlert.css b/client/components/connectionStatus/ConnectionStatusAlert.css new file mode 100644 index 00000000000..1d6931541a2 --- /dev/null +++ b/client/components/connectionStatus/ConnectionStatusAlert.css @@ -0,0 +1,19 @@ +.ConnectionStatusAlert { + + position: fixed; + z-index: 1000000; + top: 0; + + width: 100%; + padding: 2px; + + text-align: center; + + color: #916302; + border-bottom-width: 1px; + background-color: #fffdf9; +} + +.ConnectionStatusAlert__link { + color: var(--color-blue); +} diff --git a/client/components/connectionStatus/ConnectionStatusAlert.js b/client/components/connectionStatus/ConnectionStatusAlert.js new file mode 100644 index 00000000000..387ec9bc355 --- /dev/null +++ b/client/components/connectionStatus/ConnectionStatusAlert.js @@ -0,0 +1,69 @@ +import { Meteor } from 'meteor/meteor'; +import React, { useEffect, useRef, useState } from 'react'; + +import { useReactiveValue } from '../../hooks/useReactiveValue'; +import { useTranslation } from '../../hooks/useTranslation'; +import { Icon } from '../basic/Icon'; + +export function ConnectionStatusAlert() { + const { + connected, + retryTime, + status, + } = useReactiveValue(() => ({ ...Meteor.status() })); + const reconnectionTimerRef = useRef(); + const [reconnectCountdown, setReconnectCountdown] = useState(0); + const t = useTranslation(); + + useEffect(() => { + if (status === 'waiting') { + if (reconnectionTimerRef.current) { + return; + } + + reconnectionTimerRef.current = setInterval(() => { + const timeDiff = retryTime - Date.now(); + setReconnectCountdown((timeDiff > 0 && Math.round(timeDiff / 1000)) || 0); + }, 500); + return; + } + + clearInterval(reconnectionTimerRef.current); + reconnectionTimerRef.current = null; + }, [retryTime, status]); + + useEffect(() => () => { + clearInterval(reconnectionTimerRef.current); + }, []); + + if (connected) { + return null; + } + + const handleRetryClick = (event) => { + event.preventDefault(); + Meteor.reconnect(); + }; + + return
+ + {t('meteor_status', { context: status })} + + + {status === 'waiting' && <> + {' '} + {t('meteor_status_reconnect_in', { count: reconnectCountdown })} + } + + {['waiting', 'offline'].includes(status) && <> + {' '} + + {t('meteor_status_try_now', { context: status })} + + } +
; +} diff --git a/client/components/pageNotFound/PageNotFound.css b/client/components/pageNotFound/PageNotFound.css new file mode 100644 index 00000000000..416b7523afb --- /dev/null +++ b/client/components/pageNotFound/PageNotFound.css @@ -0,0 +1,44 @@ +.PageNotFound { + width: 100%; + min-height: 100vh; + padding: 10%; + + text-align: center; + + color: white; + background-color: var(--rc-color-primary); + background-image: url('/images/404.svg'); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +} + +.PageNotFound__404 { + display: block; + + padding: 10px; + + font-size: 4em; + font-weight: bold; +} + +.PageNotFound__message { + display: block; + + padding: 10px; + + font-size: 2em; + font-weight: bold; +} + +.PageNotFound__description { + display: block; + + padding: 10px; + + font-size: 1em; +} + +.PageNotFound__actions { + margin: 2rem; +} diff --git a/client/components/pageNotFound/PageNotFound.js b/client/components/pageNotFound/PageNotFound.js new file mode 100644 index 00000000000..54e6237224f --- /dev/null +++ b/client/components/pageNotFound/PageNotFound.js @@ -0,0 +1,37 @@ +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import React from 'react'; + +import { useTranslation } from '../../hooks/useTranslation'; +import { useWipeInitialPageLoading } from '../../hooks/useWipeInitialPageLoading'; +import { ConnectionStatusAlert } from '../connectionStatus/ConnectionStatusAlert'; + +export function PageNotFound() { + useWipeInitialPageLoading(); + + const t = useTranslation(); + + const handleGoToPreviousPageClick = () => { + window.history.back(); + }; + + const handleGoHomeClick = () => { + FlowRouter.go('home'); + }; + + return <> + +
+ 404 + {t('Oops_page_not_found')} + {t('Sorry_page_you_requested_does_not_exists_or_was_deleted')} + +
+ + + + +
+
+ ; +} diff --git a/client/components/setupWizard/Epilogue.css b/client/components/setupWizard/Epilogue.css new file mode 100644 index 00000000000..32853de008b --- /dev/null +++ b/client/components/setupWizard/Epilogue.css @@ -0,0 +1,81 @@ +.SetupWizard__Epilogue { + width: 930px; + padding-top: 5rem; +} + +.SetupWizard__Epilogue-header { + display: flex; + + margin: 0 -0.375rem 5rem; + align-items: center; +} + +.SetupWizard__Epilogue-headerLogo { + height: 1.5rem; + margin: 0 0.375rem; +} + +.SetupWizard__Epilogue-content { + padding: 5rem 6rem; + + border-radius: 2px; + background: #ffffff; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); +} + +.SetupWizard__Epilogue-runningHead { + display: block; + + margin-bottom: 3px; + + letter-spacing: 0.05rem; + + text-transform: uppercase; + + color: #caced1; + + font-size: 0.75rem; + + line-height: 1.125rem; +} + +.SetupWizard__Epilogue-title { + margin-bottom: 3.25rem; + + letter-spacing: 0.03rem; + + color: #2f343d; + + font-size: 2rem; + + font-weight: 600; + + line-height: 2.6rem; +} + +.SetupWizard__Epilogue-link { + display: block; + + margin-bottom: 1.5rem; + + letter-spacing: 0; + + color: var(--rc-color-button-primary); + + font-size: 1rem; + + line-height: 1.5rem; +} + +.SetupWizard__Epilogue-linkLabel { + display: block; + + letter-spacing: 0.03rem; + + text-transform: uppercase; + + color: var(--color-dark); + + font-size: 0.625rem; + line-height: 1rem; +} diff --git a/client/components/setupWizard/Epilogue.js b/client/components/setupWizard/Epilogue.js new file mode 100644 index 00000000000..1dd77db1556 --- /dev/null +++ b/client/components/setupWizard/Epilogue.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button } from '@rocket.chat/fuselage'; + +import { useTranslation } from '../../hooks/useTranslation'; +import { useSetting } from '../../hooks/useSetting'; +import { setSetting } from './functions'; +import './Epilogue.css'; + +export function Epilogue() { + const t = useTranslation(); + const siteUrl = useSetting('Site_Url'); + + const handleClick = () => { + setSetting('Show_Setup_Wizard', 'completed'); + }; + + return
+
+ +
+ +
+ {t('Launched_successfully')} +

{t('Your_workspace_is_ready')}

+ {t('Your_server_link')} + {siteUrl} + +
+
; +} diff --git a/client/components/setupWizard/Pager.js b/client/components/setupWizard/Pager.js new file mode 100644 index 00000000000..bd4d35e545b --- /dev/null +++ b/client/components/setupWizard/Pager.js @@ -0,0 +1,17 @@ +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; +import React from 'react'; + +import { useTranslation } from '../../hooks/useTranslation'; + +export function Pager({ disabled, onBackClick, isContinueEnabled = true }) { + const t = useTranslation(); + + return + {onBackClick ? : null} + + ; +} diff --git a/client/components/setupWizard/ParametersProvider.js b/client/components/setupWizard/ParametersProvider.js new file mode 100644 index 00000000000..7af9e4a8664 --- /dev/null +++ b/client/components/setupWizard/ParametersProvider.js @@ -0,0 +1,42 @@ +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; + +import { call } from '../../../app/ui-utils/client'; + +const ParametersContext = createContext({ + loaded: true, + settings: [], + canDeclineServerRegistration: false, +}); + +export const useSetupWizardParameters = () => useContext(ParametersContext); + +export function ParametersProvider({ children }) { + const [loaded, setLoaded] = useState(false); + const [settings, setSettings] = useState([]); + const [canDeclineServerRegistration, setCapableOfDeclineServerRegistration] = useState(false); + + useEffect(() => { + const getParameters = async () => { + const { + settings, + allowStandaloneServer, + } = await call('getSetupWizardParameters') || {}; + + setLoaded(true); + setSettings(settings); + setCapableOfDeclineServerRegistration(allowStandaloneServer); + }; + + getParameters(); + }, []); + + const value = useMemo(() => ({ + loaded, + settings, + canDeclineServerRegistration, + }), [loaded, settings, canDeclineServerRegistration]); + + return + {children} + ; +} diff --git a/client/components/setupWizard/SetupWizard.css b/client/components/setupWizard/SetupWizard.css new file mode 100644 index 00000000000..686744b0dd3 --- /dev/null +++ b/client/components/setupWizard/SetupWizard.css @@ -0,0 +1,18 @@ +.SetupWizard { + display: flex; + + width: 100%; + height: 100vh; + + background-color: var(--color-dark-05); + align-items: stretch; + + justify-content: center; +} + +@media (width <= 760px) { + .SetupWizard { + flex-direction: column; + justify-content: initial; + } +} diff --git a/client/components/setupWizard/SetupWizard.js b/client/components/setupWizard/SetupWizard.js new file mode 100644 index 00000000000..cb326fe467c --- /dev/null +++ b/client/components/setupWizard/SetupWizard.js @@ -0,0 +1,26 @@ +import React from 'react'; + +import { useWipeInitialPageLoading } from '../../hooks/useWipeInitialPageLoading'; +import { ConnectionStatusAlert } from '../connectionStatus/ConnectionStatusAlert'; +import { ParametersProvider } from './ParametersProvider'; +import { StateChecker } from './StateChecker'; +import { Steps } from './Steps'; +import { StepsState } from './StepsState'; +import './SetupWizard.css'; + +export function SetupWizard() { + useWipeInitialPageLoading(); + + return <> + + + + +
+ +
+
+
+
+ ; +} diff --git a/client/components/setupWizard/SideBar.css b/client/components/setupWizard/SideBar.css new file mode 100644 index 00000000000..d06ea2aeea6 --- /dev/null +++ b/client/components/setupWizard/SideBar.css @@ -0,0 +1,164 @@ +.SetupWizard__SideBar { + display: flex; + overflow: hidden; + flex: 0 1 350px; + flex-flow: column nowrap; +} + +.SetupWizard__SideBar-header { + display: flex; + + margin: 2rem 1.625rem; + align-items: center; + flex-flow: row wrap; +} + +.SetupWizard__SideBar-headerLogo { + height: 1.5rem; + margin: 0.25rem 0.375rem; +} + +.SetupWizard__SideBar-headerTag { + margin: 0.25rem 0.375rem; + + padding: 4px 8px; + + white-space: nowrap; + + text-transform: uppercase; + + color: var(--color-white); + + border-radius: 50px; + background-color: var(--color-dark); + + font-size: 10px; + + line-height: 1rem; +} + +.SetupWizard__SideBar-content { + overflow: auto; + flex: 1; + + margin: 0 0 1rem; + padding: 0 2rem; +} + +.SetupWizard__SideBar-title { + margin-bottom: 1rem; + + letter-spacing: 0.03rem; + + color: var(--color-dark); + + font-size: 2rem; + + font-weight: 600; + + line-height: 2.6rem; +} + +.SetupWizard__SideBar-text { + margin-bottom: 3rem; + + color: var(--color-gray); + + font-size: 1rem; + + font-weight: 500; + + line-height: 1.5rem; +} + +.SetupWizard__SideBar-step { + position: relative; + + margin: 0 -0.5rem 2rem; + + color: var(--color-dark-10); + + font-size: 0.875rem; + font-weight: 500; + + &::before { + display: inline-flex; + + width: 1.5rem; + height: 1.5rem; + margin: 0 0.5rem; + + content: attr(data-number); + + color: var(--color-dark-10); + + border: 1px solid var(--color-dark-10); + border-radius: 9999px; + + font-size: 0.75rem; + align-items: center; + justify-content: center; + } + + &::after { + position: absolute; + bottom: -2rem; + left: 1.2rem; + + display: block; + + width: 0.0625rem; + height: 2rem; + + content: ''; + + background-color: var(--color-dark-10); + } + + &:last-child { + margin-bottom: 0; + + &::after { + display: none; + } + } + + &--active { + color: var(--rc-color-button-primary); + + &::before { + color: var(--rc-color-button-primary); + border-color: var(--rc-color-button-primary); + background-color: transparent; + } + } + + &--past { + color: var(--rc-color-primary); + + &::before { + color: var(--rc-color-content); + border-color: var(--rc-color-button-primary); + background-color: var(--rc-color-button-primary); + } + + &::after { + background-color: var(--rc-color-button-primary); + } + } + + .rtl &::after { + right: 1.2rem; + left: auto; + } +} + +@media (width <= 760px) { + .SetupWizard__SideBar { + flex-basis: auto; + } + + .SetupWizard__SideBar-content { + display: none; + } +} diff --git a/client/components/setupWizard/SideBar.js b/client/components/setupWizard/SideBar.js new file mode 100644 index 00000000000..84511553290 --- /dev/null +++ b/client/components/setupWizard/SideBar.js @@ -0,0 +1,39 @@ +import React from 'react'; + +import { useTranslation } from '../../hooks/useTranslation'; +import { useSetupWizardStepsState } from './StepsState'; +import './SideBar.css'; + +export function SideBar({ steps = [] }) { + const { currentStep } = useSetupWizardStepsState(); + + const t = useTranslation(); + + return ; +} diff --git a/client/components/setupWizard/StateChecker.js b/client/components/setupWizard/StateChecker.js new file mode 100644 index 00000000000..e4bd09467b4 --- /dev/null +++ b/client/components/setupWizard/StateChecker.js @@ -0,0 +1,47 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; +import React, { useEffect, useState } from 'react'; + +import { hasRole } from '../../../app/authorization'; +import { Users } from '../../../app/models'; +import { useSetting } from '../../hooks/useSetting'; +import { useUserId } from '../../hooks/useUserId'; +import { useReactiveValue } from '../../hooks/useReactiveValue'; + +export function StateChecker({ children }) { + const setupWizardState = useSetting('Show_Setup_Wizard'); + const userId = useUserId(); + const user = useReactiveValue(() => Users.findOne(userId, { fields: { status: true } }), [userId]); + + const [renderAllowed, allowRender] = useState(false); + + useEffect(() => { + if (!setupWizardState) { + return; + } + + if (userId && (!user || !user.status)) { + return; + } + + const isComplete = setupWizardState === 'completed'; + const noUserLoggedInAndIsNotPending = !renderAllowed && !user && setupWizardState !== 'pending'; + const userIsLoggedInButIsNotAdmin = !!user && !hasRole(user._id, 'admin'); + + const mustRedirect = isComplete || noUserLoggedInAndIsNotPending || userIsLoggedInButIsNotAdmin; + + if (mustRedirect) { + FlowRouter.withReplaceState(() => { + FlowRouter.go('home'); + }); + return; + } + + allowRender(true); + }, [setupWizardState, userId, user]); + + if (!renderAllowed) { + return null; + } + + return <>{children}; +} diff --git a/client/components/setupWizard/Step.css b/client/components/setupWizard/Step.css new file mode 100644 index 00000000000..5554bcb10c7 --- /dev/null +++ b/client/components/setupWizard/Step.css @@ -0,0 +1,26 @@ +.SetupWizard__Step { + display: none; + + flex-direction: column; + + max-width: 350px; + margin: 2rem auto; + + transition: opacity 1s; + + opacity: 1; + + &--active { + display: flex; + } + + &--working { + opacity: 0.5; + } +} + +@media (width <= 760px) { + .SetupWizard__Step { + margin: 0 auto; + } +} diff --git a/client/components/setupWizard/Step.js b/client/components/setupWizard/Step.js new file mode 100644 index 00000000000..fd2c6c85393 --- /dev/null +++ b/client/components/setupWizard/Step.js @@ -0,0 +1,14 @@ +import React from 'react'; + +import './Step.css'; + +export const Step = ({ active, working = false, ...props }) => +
; diff --git a/client/components/setupWizard/StepContent.css b/client/components/setupWizard/StepContent.css new file mode 100644 index 00000000000..1af1de1f1a1 --- /dev/null +++ b/client/components/setupWizard/StepContent.css @@ -0,0 +1,3 @@ +.SetupWizard__StepContent { + margin-bottom: 2rem; +} diff --git a/client/components/setupWizard/StepContent.js b/client/components/setupWizard/StepContent.js new file mode 100644 index 00000000000..910ce2e726f --- /dev/null +++ b/client/components/setupWizard/StepContent.js @@ -0,0 +1,7 @@ +import React from 'react'; + +import './StepContent.css'; + +export function StepContent(props) { + return
; +} diff --git a/client/components/setupWizard/StepHeader.css b/client/components/setupWizard/StepHeader.css new file mode 100644 index 00000000000..23065ee4d16 --- /dev/null +++ b/client/components/setupWizard/StepHeader.css @@ -0,0 +1,28 @@ +.SetupWizard__StepHeader { + margin-bottom: 2rem; +} + +.SetupWizard__StepHeader-runningHead { + margin-bottom: 3px; + + letter-spacing: 0.05rem; + + text-transform: uppercase; + + color: var(--color-dark-20); + + font-size: 0.75rem; + + line-height: 1.125rem; +} + +.SetupWizard__StepHeader-title { + letter-spacing: 0.05rem; + + color: var(--color-dark-90); + + font-size: 1.25rem; + font-weight: 500; + + line-height: 1.75rem; +} diff --git a/client/components/setupWizard/StepHeader.js b/client/components/setupWizard/StepHeader.js new file mode 100644 index 00000000000..15a414fbb65 --- /dev/null +++ b/client/components/setupWizard/StepHeader.js @@ -0,0 +1,13 @@ +import React from 'react'; + +import { useTranslation } from '../../hooks/useTranslation'; +import './StepHeader.css'; + +export function StepHeader({ number, title }) { + const t = useTranslation(); + + return
+

{t('Step')} {number}

+

{title}

+
; +} diff --git a/client/components/setupWizard/Steps.css b/client/components/setupWizard/Steps.css new file mode 100644 index 00000000000..19d9938f84a --- /dev/null +++ b/client/components/setupWizard/Steps.css @@ -0,0 +1,25 @@ +.SetupWizard__Steps { + flex: 1 1 auto; + + height: 100vh; + padding: 1rem; +} + +.SetupWizard__Steps-wrapper { + overflow: auto; + + width: 100%; + height: 100%; + padding: 1rem 3rem; + + border-radius: 2px; + background-color: var(--color-white); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); + justify-content: center; +} + +@media (width <= 760px) { + .SetupWizard__Steps { + padding: 0; + } +} diff --git a/client/components/setupWizard/Steps.js b/client/components/setupWizard/Steps.js new file mode 100644 index 00000000000..3dab7fab351 --- /dev/null +++ b/client/components/setupWizard/Steps.js @@ -0,0 +1,49 @@ +import React from 'react'; + +import { AdminUserInformationStep } from './steps/AdminUserInformationStep'; +import { SettingsBasedStep } from './steps/SettingsBasedStep'; +import { RegisterServerStep } from './steps/RegisterServerStep'; +import { useTranslation } from '../../hooks/useTranslation'; +import { Epilogue } from './Epilogue'; +import { SideBar } from './SideBar'; +import { useSetupWizardStepsState, finalStep } from './StepsState'; + +export function Steps() { + const { currentStep } = useSetupWizardStepsState(); + const t = useTranslation(); + + if (currentStep === finalStep) { + return ; + } + + return <> + +
+
+ + + + +
+
+ ; +} diff --git a/client/components/setupWizard/StepsState.js b/client/components/setupWizard/StepsState.js new file mode 100644 index 00000000000..0dc6b08585c --- /dev/null +++ b/client/components/setupWizard/StepsState.js @@ -0,0 +1,57 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; + +import { useUserId } from '../../hooks/useUserId'; + +const Context = createContext(); + +export const useSetupWizardStepsState = () => useContext(Context); + +export const finalStep = 'final'; + +const useStepRouting = () => { + const userId = useUserId(); + const [currentStep, setCurrentStep] = useState(() => { + const param = FlowRouter.getParam('step'); + + if (param === finalStep) { + return finalStep; + } + + const step = parseInt(param, 10); + if (Number.isFinite(step) && step >= 1) { + return step; + } + + return 1; + }); + + useEffect(() => { + if (!userId) { + setCurrentStep(1); + } else if (currentStep === 1) { + setCurrentStep(2); + } + + FlowRouter.withReplaceState(() => { + FlowRouter.go('setup-wizard', { step: String(currentStep) }); + }); + }, [userId, currentStep]); + + return [currentStep, setCurrentStep]; +}; + +export function StepsState({ children }) { + const [currentStep, setCurrentStep] = useStepRouting(); + + const value = useMemo(() => ({ + currentStep, + goToPreviousStep: () => setCurrentStep(currentStep - 1), + goToNextStep: () => setCurrentStep(currentStep + 1), + goToFinalStep: () => setCurrentStep(finalStep), + }), [currentStep]); + + return + {children} + ; +} diff --git a/client/components/setupWizard/functions.js b/client/components/setupWizard/functions.js new file mode 100644 index 00000000000..d96e0a33e90 --- /dev/null +++ b/client/components/setupWizard/functions.js @@ -0,0 +1,20 @@ +import { Meteor } from 'meteor/meteor'; + +import { settings } from '../../../app/settings/lib/settings'; + +const withPromisifiedReturn = (f) => (...args) => new Promise((resolve, reject) => { + f(...args, (error, ...returnedValues) => { + if (error) { + reject(error); + return; + } + + resolve(returnedValues); + }); +}); + +export const loginWithPassword = withPromisifiedReturn(Meteor.loginWithPassword.bind(Meteor)); + +export const batchSetSettings = withPromisifiedReturn(settings.batchSet.bind(settings)); + +export const setSetting = withPromisifiedReturn(settings.set.bind(settings)); diff --git a/client/components/setupWizard/steps/AdminUserInformationStep.js b/client/components/setupWizard/steps/AdminUserInformationStep.js new file mode 100644 index 00000000000..5d272dd3947 --- /dev/null +++ b/client/components/setupWizard/steps/AdminUserInformationStep.js @@ -0,0 +1,156 @@ +import { Input, InputGroup } from '@rocket.chat/fuselage'; +import { Session } from 'meteor/session'; +import React, { useMemo, useState } from 'react'; +import toastr from 'toastr'; + +import { call } from '../../../../app/ui-utils/client'; +import { handleError } from '../../../../app/utils/client'; +import { callbacks } from '../../../../app/callbacks/client'; +import { useSetting } from '../../../hooks/useSetting'; +import { useTranslation } from '../../../hooks/useTranslation'; +import { useSetupWizardStepsState } from '../StepsState'; +import { Step } from '../Step'; +import { StepHeader } from '../StepHeader'; +import { Pager } from '../Pager'; +import { StepContent } from '../StepContent'; +import { loginWithPassword } from '../functions'; +import { useFocus } from '../../../hooks/useFocus'; + +const registerAdminUser = async ({ name, username, email, password, onRegistrationEmailSent }) => { + await call('registerUser', { name, username, email, pass: password }); + callbacks.run('userRegistered'); + + try { + await loginWithPassword(email, password); + } catch (error) { + if (error.error === 'error-invalid-email') { + onRegistrationEmailSent && onRegistrationEmailSent(); + return; + } + + throw error; + } + + Session.set('forceLogin', false); + + await call('setUsername', username); + + callbacks.run('usernameSet'); +}; + +export function AdminUserInformationStep({ step, title }) { + const { currentStep, goToNextStep } = useSetupWizardStepsState(); + const active = step === currentStep; + + const regexpForUsernameValidation = useSetting('UTF8_Names_Validation'); + const usernameRegExp = useMemo(() => new RegExp(`^${ regexpForUsernameValidation }$`), [regexpForUsernameValidation]); + const emailRegExp = useMemo(() => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i, []); + + const [name, setName] = useState(''); + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const [isNameValid, validateName] = useState(true); + const [isUsernameValid, validateUsername] = useState(true); + const [isEmailValid, validateEmail] = useState(true); + const [isPasswordValid, validatePassword] = useState(true); + + const isContinueEnabled = useMemo(() => name && username && email && password, [name, username, email, password]); + + const [commiting, setCommiting] = useState(false); + + const validate = () => { + const isNameValid = !!name; + const isUsernameValid = !!username && usernameRegExp.test(username); + const isEmailValid = !!email && emailRegExp.test(email); + const isPasswordValid = !!password; + + validateName(isNameValid); + validateUsername(isUsernameValid); + validateEmail(isEmailValid); + validatePassword(isPasswordValid); + + return isNameValid && isUsernameValid && isEmailValid && isPasswordValid; + }; + + const t = useTranslation(); + + const autoFocusRef = useFocus(active); + + const handleSubmit = async (event) => { + event.preventDefault(); + + const canRegisterAdminUser = validate(); + + if (!canRegisterAdminUser) { + return; + } + + setCommiting(true); + + try { + await registerAdminUser({ + name, + username, + email, + password, + onRegistrationEmailSent: () => toastr.success(t('We_have_sent_registration_email')), + }); + goToNextStep(); + } catch (error) { + console.error(error); + handleError(error); + } finally { + setCommiting(false); + } + }; + + return + + + + + setName(value)} + error={!isNameValid} + /> + setUsername(value)} + error={!isUsernameValid && t('Invalid_username')} + /> + setEmail(value)} + error={!isEmailValid && t('Invalid_email')} + /> + setPassword(value)} + error={!isPasswordValid} + /> + + + + + ; +} diff --git a/client/components/setupWizard/steps/RegisterServerStep.css b/client/components/setupWizard/steps/RegisterServerStep.css new file mode 100644 index 00000000000..5ff7379810f --- /dev/null +++ b/client/components/setupWizard/steps/RegisterServerStep.css @@ -0,0 +1,81 @@ +.SetupWizard__RegisterServerStep-text { + margin-bottom: 3rem; + + color: var(--color-gray); + + font-size: 1rem; + + font-weight: 500; + + line-height: 1.5rem; +} + +.SetupWizard__RegisterServerStep-content { + display: flex; + flex-direction: column; +} + +.SetupWizard__RegisterServerStep-option { + display: block; + + padding: 1.5rem; + + cursor: pointer; + + color: var(--color-dark); + border: 2px solid var(--color-dark-10); + + border-radius: 2px; + + font-size: 0.875rem; + + line-height: 1.25rem; + + &:first-child { + margin-bottom: 1rem; + } + + &--selected { + border-color: var(--rc-color-button-primary); + } + + &--disabled { + opacity: 0.25; + } +} + +.SetupWizard__RegisterServerStep-items { + margin: 1rem 0; + + & + * { + margin-top: 1rem; + } +} + +.SetupWizard__RegisterServerStep-item { + display: flex; + + margin: 0 -0.5rem 0.5rem; + align-items: center; + + &:last-child { + margin-bottom: 0; + } + + & > .SetupWizard__RegisterServerStep-item-icon { + width: 20px; + min-width: 20px; + height: 20px; + margin: 0 0.5rem; + align-self: baseline; + + &--check { + color: var(--rc-color-button-primary); + } + + &--circle { + height: 6px; + margin: 7px 0.5rem; + } + } +} diff --git a/client/components/setupWizard/steps/RegisterServerStep.js b/client/components/setupWizard/steps/RegisterServerStep.js new file mode 100644 index 00000000000..d45f658bd5f --- /dev/null +++ b/client/components/setupWizard/steps/RegisterServerStep.js @@ -0,0 +1,152 @@ +import { CheckBox, RadioButton } from '@rocket.chat/fuselage'; +import React, { useState } from 'react'; + +import { call } from '../../../../app/ui-utils/client'; +import { handleError } from '../../../../app/utils/client'; +import { useTranslation } from '../../../hooks/useTranslation'; +import { Icon } from '../../basic/Icon'; +import { Pager } from '../Pager'; +import { useSetupWizardParameters } from '../ParametersProvider'; +import { Step } from '../Step'; +import { StepContent } from '../StepContent'; +import { StepHeader } from '../StepHeader'; +import { useSetupWizardStepsState } from '../StepsState'; +import { batchSetSettings } from '../functions'; +import { useFocus } from '../../../hooks/useFocus'; + +const Option = React.forwardRef(({ children, label, selected, disabled, ...props }, ref) => + +); + +const Items = (props) =>
    ; + +const Item = ({ children, icon, ...props }) => +
  • + + {children} +
  • ; + +export function RegisterServerStep({ step, title }) { + const { canDeclineServerRegistration } = useSetupWizardParameters(); + const { currentStep, goToPreviousStep, goToFinalStep } = useSetupWizardStepsState(); + + const active = step === currentStep; + + const [registerServer, setRegisterServer] = useState(true); + const [optInMarketingEmails, setOptInMarketingEmails] = useState(true); + + const t = useTranslation(); + + const [commiting, setComitting] = useState(false); + + const handleBackClick = () => { + goToPreviousStep(); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + + setComitting(true); + + try { + await batchSetSettings([ + { + _id: 'Statistics_reporting', + value: registerServer, + }, + { + _id: 'Apps_Framework_enabled', + value: registerServer, + }, + { + _id: 'Register_Server', + value: registerServer, + }, + { + _id: 'Allow_Marketing_Emails', + value: optInMarketingEmails, + }, + ]); + + if (registerServer) { + await call('cloud:registerWorkspace'); + } + + setComitting(false); + goToFinalStep(); + } catch (error) { + console.error(error); + handleError(error); + setComitting(false); + } + }; + + const autoFocusRef = useFocus(active); + + return + + + +

    {t('Register_Server_Info')}

    + +
    + + +
    +
    + + +
    ; +} diff --git a/client/components/setupWizard/steps/SettingsBasedStep.js b/client/components/setupWizard/steps/SettingsBasedStep.js new file mode 100644 index 00000000000..1bc38d01c28 --- /dev/null +++ b/client/components/setupWizard/steps/SettingsBasedStep.js @@ -0,0 +1,141 @@ +import { Input, InputGroup } from '@rocket.chat/fuselage'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import React, { Fragment, useEffect, useReducer, useState } from 'react'; + +import { handleError } from '../../../../app/utils/client'; +import { useTranslation } from '../../../hooks/useTranslation'; +import { useReactiveValue } from '../../../hooks/useReactiveValue'; +import { Pager } from '../Pager'; +import { useSetupWizardParameters } from '../ParametersProvider'; +import { useSetupWizardStepsState } from '../StepsState'; +import { Step } from '../Step'; +import { StepHeader } from '../StepHeader'; +import { StepContent } from '../StepContent'; +import { batchSetSettings } from '../functions'; +import { useFocus } from '../../../hooks/useFocus'; + +const useFields = () => { + const reset = 'RESET'; + const setValue = 'SET_VALUE'; + + const [fields, dispatch] = useReducer((fields, { type, payload }) => { + if (type === reset) { + return payload; + } + + if (type === setValue) { + const { _id, value } = payload; + return fields.map((field) => (field._id === _id ? { ...field, value } : field)); + } + + return fields; + }, []); + + const resetFields = (fields) => dispatch({ type: reset, payload: fields }); + const setFieldValue = (_id, value) => dispatch({ type: setValue, payload: { _id, value } }); + + return { fields, resetFields, setFieldValue }; +}; + +export function SettingsBasedStep({ step, title }) { + const { settings } = useSetupWizardParameters(); + const { currentStep, goToPreviousStep, goToNextStep } = useSetupWizardStepsState(); + const { fields, resetFields, setFieldValue } = useFields(); + const [commiting, setCommiting] = useState(false); + + const active = step === currentStep; + + const languages = useReactiveValue(() => TAPi18n.getLanguages(), []); + + useEffect(() => { + resetFields( + settings + .filter(({ wizard }) => wizard.step === step) + .filter(({ type }) => ['string', 'select', 'language'].includes(type)) + .sort(({ wizard: { order: a } }, { wizard: { order: b } }) => a - b) + .map(({ value, ...field }) => ({ ...field, value: value || '' })) + ); + }, [settings, currentStep]); + + const t = useTranslation(); + + const handleBackClick = () => { + goToPreviousStep(); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + + setCommiting(true); + + try { + await batchSetSettings(fields.map(({ _id, value }) => ({ _id, value }))); + goToNextStep(); + } catch (error) { + console.error(error); + handleError(error); + } finally { + setCommiting(false); + } + }; + + const autoFocusRef = useFocus(active); + + return + + + + + {fields.map(({ _id, type, i18nLabel, value, values }, i) => + + {type === 'string' + && setFieldValue(_id, value)} + />} + + {type === 'select' + && setFieldValue(_id, value)} + > + {values + .map(({ i18nLabel, key }) => ({ label: t(i18nLabel), value: key })) + .map(({ label, value }) => )} + } + + {type === 'language' + && setFieldValue(_id, value)} + > + {Object.entries(languages) + .map(([key, { name }]) => ({ label: name, value: key })) + .sort((a, b) => a.key - b.key) + .map(({ label, value }) => )} + } + + )} + + + + 2 && handleBackClick} + /> + ; +} diff --git a/client/hooks/useFocus.js b/client/hooks/useFocus.js new file mode 100644 index 00000000000..74282ecdfae --- /dev/null +++ b/client/hooks/useFocus.js @@ -0,0 +1,15 @@ +import { useEffect, useState } from 'react'; + +export const useFocus = (isFocused) => { + const [element, setElement] = useState(null); + + useEffect(() => { + if (isFocused && element) { + element.focus(); + } + }, [element, isFocused]); + + return (ref) => { + setElement(ref); + }; +}; diff --git a/client/hooks/useReactiveValue.js b/client/hooks/useReactiveValue.js new file mode 100644 index 00000000000..00e63cad57c --- /dev/null +++ b/client/hooks/useReactiveValue.js @@ -0,0 +1,19 @@ +import { Tracker } from 'meteor/tracker'; +import { useEffect, useState } from 'react'; + +export const useReactiveValue = (getValue, deps = []) => { + const [value, setValue] = useState(getValue); + + useEffect(() => { + const computation = Tracker.autorun(() => { + const newValue = getValue(); + setValue(() => newValue); + }); + + return () => { + computation.stop(); + }; + }, deps); + + return value; +}; diff --git a/client/hooks/useSession.js b/client/hooks/useSession.js new file mode 100644 index 00000000000..731d2226510 --- /dev/null +++ b/client/hooks/useSession.js @@ -0,0 +1,5 @@ +import { Session } from 'meteor/session'; + +import { useReactiveValue } from './useReactiveValue'; + +export const useSession = (variableName) => useReactiveValue(() => Session.get(variableName)); diff --git a/client/hooks/useSetting.js b/client/hooks/useSetting.js new file mode 100644 index 00000000000..1d287c2e819 --- /dev/null +++ b/client/hooks/useSetting.js @@ -0,0 +1,4 @@ +import { settings } from '../../app/settings/client'; +import { useReactiveValue } from './useReactiveValue'; + +export const useSetting = (settingName) => useReactiveValue(() => settings.get(settingName)); diff --git a/client/hooks/useTranslation.js b/client/hooks/useTranslation.js new file mode 100644 index 00000000000..efeb945795f --- /dev/null +++ b/client/hooks/useTranslation.js @@ -0,0 +1,21 @@ +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { Tracker } from 'meteor/tracker'; + +import { useReactiveValue } from './useReactiveValue'; + +const translator = (key, ...replaces) => Tracker.nonreactive(() => { + if (typeof replaces[0] === 'object') { + return TAPi18n.__(key, ...replaces); + } + + return TAPi18n.__(key, { + postProcess: 'sprintf', + sprintf: replaces, + }); +}); + +export const useTranslation = () => { + useReactiveValue(() => TAPi18n.getLanguage()); + + return translator; +}; diff --git a/client/hooks/useUserId.js b/client/hooks/useUserId.js new file mode 100644 index 00000000000..600efa45f51 --- /dev/null +++ b/client/hooks/useUserId.js @@ -0,0 +1,5 @@ +import { Meteor } from 'meteor/meteor'; + +import { useReactiveValue } from './useReactiveValue'; + +export const useUserId = () => useReactiveValue(() => Meteor.userId()); diff --git a/client/hooks/useWipeInitialPageLoading.js b/client/hooks/useWipeInitialPageLoading.js new file mode 100644 index 00000000000..2c8ec5a6afd --- /dev/null +++ b/client/hooks/useWipeInitialPageLoading.js @@ -0,0 +1,17 @@ +import { useLayoutEffect } from 'react'; + +export const useWipeInitialPageLoading = () => { + useLayoutEffect(() => { + const initialPageLoadingElement = document.getElementById('initial-page-loading'); + + if (!initialPageLoadingElement) { + return; + } + + initialPageLoadingElement.style.display = 'none'; + + return () => { + initialPageLoadingElement.style.display = 'flex'; + }; + }, []); +}; diff --git a/client/importPackages.js b/client/importPackages.js index a2a44dfe298..14f6e227977 100644 --- a/client/importPackages.js +++ b/client/importPackages.js @@ -54,7 +54,6 @@ import '../app/oembed/client'; import '../app/otr/client'; import '../app/push-notifications/client'; import '../app/apps/client'; -import '../app/setup-wizard/client'; import '../app/slackbridge/client'; import '../app/slashcommands-archiveroom/client'; import '../app/slashcommand-asciiarts/client'; diff --git a/client/importsCss.js b/client/importsCss.js index 9b3b49c4fd6..34cf7a0a1c0 100644 --- a/client/importsCss.js +++ b/client/importsCss.js @@ -1,4 +1,4 @@ - +import './RocketChat.font.css'; import '../app/chatpal-search/client/style.css'; import '../app/theme/client/main.css'; import '../app/theme/client/vendor/photoswipe.css'; diff --git a/client/main.js b/client/main.js index 7b0799357d6..4f58796f67c 100644 --- a/client/main.js +++ b/client/main.js @@ -21,7 +21,6 @@ import './notifications/updateAvatar'; import './notifications/updateUserState'; import './notifications/UsersNameChanged'; import './routes/adminRouter'; -import './routes/pageNotFound'; import './routes/roomRoute'; import './routes/router'; import './startup/emailVerification'; diff --git a/client/routes/pageNotFound.html b/client/routes/pageNotFound.html deleted file mode 100644 index 6a5bfffbeb6..00000000000 --- a/client/routes/pageNotFound.html +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/client/routes/pageNotFound.js b/client/routes/pageNotFound.js deleted file mode 100644 index e83fe3f0271..00000000000 --- a/client/routes/pageNotFound.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Template } from 'meteor/templating'; -import { FlowRouter } from 'meteor/kadira:flow-router'; - -import './pageNotFound.html'; - -Template.pageNotFound.onRendered(function() { - const parent = document.querySelector('.page-loading'); - const child = document.querySelector('.loading-animation'); - parent.removeChild(child); -}); - -Template.pageNotFound.events({ - 'click .page-not-found-button-home'(e) { - e.preventDefault(); - FlowRouter.go('home'); - }, - - 'click .page-not-found-button-previous'(e) { - e.preventDefault(); - window.history.back(); - }, -}); diff --git a/client/routes/router.js b/client/routes/router.js index 4df791c5cf8..3159fc618c5 100644 --- a/client/routes/router.js +++ b/client/routes/router.js @@ -3,8 +3,10 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { Tracker } from 'meteor/tracker'; import { Blaze } from 'meteor/blaze'; +import { HTML } from 'meteor/htmljs'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { BlazeLayout } from 'meteor/kadira:blaze-layout'; +import { Template } from 'meteor/templating'; import { Session } from 'meteor/session'; import { KonchatNotification } from '../../app/ui'; @@ -15,6 +17,42 @@ Blaze.registerHelper('pathFor', function(path, kw) { BlazeLayout.setRoot('body'); +const createTemplateForComponent = async ( + component, + props = {}, + // eslint-disable-next-line new-cap + renderContainerView = () => HTML.DIV() +) => { + const React = await import('react'); + const ReactDOM = await import('react-dom'); + + const name = component.displayName || component.name; + + if (!name) { + throw new Error('the component must have a name'); + } + + Template[name] = new Blaze.Template(name, renderContainerView); + + Template[name].onRendered(() => { + Template.instance().autorun((computation) => { + if (computation.firstRun) { + Template.instance().container = Template.instance().firstNode; + } + + ReactDOM.render(React.createElement(component, props), Template.instance().firstNode); + }); + }); + + Template[name].onDestroyed(() => { + if (Template.instance().container) { + ReactDOM.unmountComponentAtNode(Template.instance().container); + } + }); + + return name; +}; + FlowRouter.subscriptions = function() { Tracker.autorun(() => { if (Meteor.userId()) { @@ -137,14 +175,6 @@ FlowRouter.route('/room-not-found/:type/:name', { }, }); -FlowRouter.route('/fxos', { - name: 'firefox-os-install', - - action() { - BlazeLayout.render('fxOsInstallPrompt'); - }, -}); - FlowRouter.route('/register/:hash', { name: 'register-secret-url', @@ -164,24 +194,17 @@ FlowRouter.route('/register/:hash', { }, }); -FlowRouter.route('/setup-wizard', { +FlowRouter.route('/setup-wizard/:step?', { name: 'setup-wizard', - - action() { - BlazeLayout.render('setupWizard'); - }, -}); - -FlowRouter.route('/setup-wizard/final', { - name: 'setup-wizard-final', - - action() { - BlazeLayout.render('setupWizardFinal'); + action: async () => { + const { SetupWizard } = await import('../components/setupWizard/SetupWizard'); + BlazeLayout.render(await createTemplateForComponent(SetupWizard)); }, }); FlowRouter.notFound = { - action() { - BlazeLayout.render('pageNotFound'); + action: async () => { + const { PageNotFound } = await import('../components/pageNotFound/PageNotFound'); + BlazeLayout.render(await createTemplateForComponent(PageNotFound)); }, }; diff --git a/client/routes/stylesheets/pageNotFound.css b/client/routes/stylesheets/pageNotFound.css deleted file mode 100644 index 6e0aae972be..00000000000 --- a/client/routes/stylesheets/pageNotFound.css +++ /dev/null @@ -1,48 +0,0 @@ -.page-not-found-container { - width: 100%; - height: 100%; - padding: 10%; - - text-align: center; - - color: white; - background-image: url("/images/404.svg"); - background-repeat: no-repeat; - background-position: center; - background-size: cover; - - .error-404 { - display: block; - - padding: 10px; - - font-size: 4em; - font-weight: bold; - } - - .page-not-found-message { - display: block; - - padding: 10px; - - font-size: 2em; - font-weight: bold; - } - - .page-not-found-description { - display: block; - - padding: 10px; - - font-size: 1em; - } - - .page-not-found-button { - display: inline-block; - - margin: 20px 10px 0; - padding: 8px 16px; - - font-size: 1em; - } -} diff --git a/package-lock.json b/package-lock.json index e125255c262..ff9de5e8246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -702,6 +702,19 @@ "eslint-plugin-import": "^2.17.2" } }, + "@rocket.chat/fuselage": { + "version": "0.2.0-alpha.2", + "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.2.0-alpha.2.tgz", + "integrity": "sha512-fAGRMPGvluQAxwUCWmgn28anFT0NPsKEzM1yYOdo1xPbYbs48QKg6f9aUgNEAAqpviw58AKq8d/re8KPjzEYLw==", + "requires": { + "@rocket.chat/icons": "^0.2.0-alpha.0" + } + }, + "@rocket.chat/icons": { + "version": "0.2.0-alpha.0", + "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.2.0-alpha.0.tgz", + "integrity": "sha512-7bzc7frrOfSypD/QvJ+eBfiD5XZSpxzN8taaOB2g0AHDUYcS/yIDpZLRH0Eq0fVe4A4gXsg0MRgcU0cJzuXQ+g==" + }, "@rocket.chat/livechat": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.1.6.tgz", @@ -882,7 +895,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" }, "@types/express": { @@ -1859,7 +1872,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "requires": { "follow-redirects": "^1.3.0", @@ -2197,7 +2210,7 @@ }, "babel-plugin-add-module-exports": { "version": "0.2.1", - "resolved": "http://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=", "dev": true }, @@ -2218,79 +2231,79 @@ }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", "dev": true }, "babel-plugin-syntax-async-generators": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", "dev": true }, "babel-plugin-syntax-class-constructor-call": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", "dev": true }, "babel-plugin-syntax-class-properties": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", "dev": true }, "babel-plugin-syntax-decorators": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", "dev": true }, "babel-plugin-syntax-do-expressions": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=", "dev": true }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, "babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "dev": true }, "babel-plugin-syntax-export-extensions": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", "dev": true }, "babel-plugin-syntax-flow": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", "dev": true }, "babel-plugin-syntax-function-bind": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=", "dev": true }, "babel-plugin-syntax-jsx": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", "dev": true }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", "dev": true }, @@ -2687,7 +2700,7 @@ }, "babel-preset-es2015": { "version": "6.3.13", - "resolved": "http://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.3.13.tgz", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.3.13.tgz", "integrity": "sha1-l9zn7ykuGMubK3VF2AxZPCjZUX8=", "dev": true, "requires": { @@ -2715,7 +2728,7 @@ }, "babel-preset-react": { "version": "6.3.13", - "resolved": "http://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.3.13.tgz", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.3.13.tgz", "integrity": "sha1-E9VeBqZfqqoHw5v2Op2DbgMhFvo=", "dev": true, "requires": { @@ -2729,7 +2742,7 @@ }, "babel-preset-stage-0": { "version": "6.3.13", - "resolved": "http://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.3.13.tgz", + "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.3.13.tgz", "integrity": "sha1-eKN8VvCzmI8qeZMtywzrj/N3sNE=", "dev": true, "requires": { @@ -3636,7 +3649,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "requires": { "readable-stream": "^2.3.5", @@ -4020,7 +4033,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -4386,7 +4399,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -4489,7 +4502,7 @@ }, "chimp": { "version": "0.51.1", - "resolved": "http://registry.npmjs.org/chimp/-/chimp-0.51.1.tgz", + "resolved": "https://registry.npmjs.org/chimp/-/chimp-0.51.1.tgz", "integrity": "sha1-6hIbzfJsidV/jvNBlUDPPCeaPMU=", "dev": true, "requires": { @@ -4535,7 +4548,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -4607,7 +4620,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -4659,7 +4672,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -4682,7 +4695,7 @@ }, "chokidar": { "version": "1.6.1", - "resolved": "http://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", "integrity": "sha1-L0RHq16W5Q+z14n9kNTHLg5McMI=", "dev": true, "requires": { @@ -5448,7 +5461,7 @@ "dependencies": { "core-js": { "version": "1.2.7", - "resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" } } @@ -5848,7 +5861,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5878,7 +5891,7 @@ }, "deprecate": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/deprecate/-/deprecate-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.0.0.tgz", "integrity": "sha1-ZhSQ7SQokWpsiIPYg05WRvTkpKg=" }, "deprecated-decorator": { @@ -5936,7 +5949,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -6003,7 +6016,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" } } @@ -6381,7 +6394,7 @@ }, "es6-promisify": { "version": "5.0.0", - "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { "es6-promise": "^4.0.3" @@ -6717,6 +6730,67 @@ } } }, + "eslint-plugin-react": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, "eslint-scope": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", @@ -6822,7 +6896,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "evp_bytestokey": { @@ -7240,7 +7314,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -7410,13 +7484,13 @@ "dependencies": { "lodash": { "version": "2.4.2", - "resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", "dev": true }, "underscore.string": { "version": "2.3.3", - "resolved": "http://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", "dev": true } @@ -8597,7 +8671,7 @@ "dependencies": { "minimist": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", "dev": true } @@ -9452,7 +9526,7 @@ }, "hapi": { "version": "8.8.0", - "resolved": "http://registry.npmjs.org/hapi/-/hapi-8.8.0.tgz", + "resolved": "https://registry.npmjs.org/hapi/-/hapi-8.8.0.tgz", "integrity": "sha1-h+N6Bum0meiXkOLcERqpZotuYX8=", "dev": true, "requires": { @@ -9522,7 +9596,7 @@ }, "catbox": { "version": "4.3.0", - "resolved": "http://registry.npmjs.org/catbox/-/catbox-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/catbox/-/catbox-4.3.0.tgz", "integrity": "sha1-IiN3vWfxKRrA4l0AAC0GWp3385o=", "dev": true, "requires": { @@ -9619,7 +9693,7 @@ }, "joi": { "version": "6.4.1", - "resolved": "http://registry.npmjs.org/joi/-/joi-6.4.1.tgz", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.4.1.tgz", "integrity": "sha1-9Q9CRTVgBo5jg9oVrC0w3Xzra24=", "dev": true, "requires": { @@ -9631,7 +9705,7 @@ "dependencies": { "isemail": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz", "integrity": "sha1-4Mj23D9HCX53dzlcaJYnGqJWw7U=", "dev": true }, @@ -9664,7 +9738,7 @@ "dependencies": { "mime-db": { "version": "1.14.0", - "resolved": "http://registry.npmjs.org/mime-db/-/mime-db-1.14.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.14.0.tgz", "integrity": "sha1-1WHxC27mbbUflK5leilRp0IX7YM=", "dev": true } @@ -10405,7 +10479,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -10757,7 +10831,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -10924,7 +10998,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-object": { @@ -11097,7 +11171,7 @@ }, "isemail": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" }, "isexe": { @@ -11133,7 +11207,7 @@ }, "jasmine-core": { "version": "2.99.1", - "resolved": "http://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", "dev": true }, @@ -11215,8 +11289,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.12.0", @@ -11339,7 +11412,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -11402,6 +11475,16 @@ "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz", "integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY=" }, + "jsx-ast-utils": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", + "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" + } + }, "juice": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/juice/-/juice-5.2.0.tgz", @@ -11632,7 +11715,7 @@ }, "promise": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", "optional": true, "requires": { @@ -12095,7 +12178,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -12304,7 +12386,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -12813,14 +12895,14 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { @@ -12908,7 +12990,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -13345,7 +13427,7 @@ }, "ncp": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, @@ -13715,7 +13797,7 @@ }, "npm-install-package": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/npm-install-package/-/npm-install-package-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/npm-install-package/-/npm-install-package-2.1.0.tgz", "integrity": "sha1-1+/jz816sAYUuJbqUxGdyaslkSU=", "dev": true }, @@ -13730,7 +13812,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -13840,6 +13922,18 @@ "has": "^1.0.1" } }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.11.0", + "function-bind": "^1.1.1", + "has": "^1.0.1" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -13969,7 +14063,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.0" @@ -14302,7 +14396,7 @@ }, "es6-promise": { "version": "4.0.5", - "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", "dev": true }, @@ -14358,7 +14452,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -15352,6 +15446,16 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "protobufjs": { "version": "6.8.8", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", @@ -15596,11 +15700,38 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, + "react": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-dom": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", + "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15612,7 +15743,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -15646,7 +15777,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -15709,7 +15840,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -15847,7 +15978,7 @@ }, "regjsgen": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, @@ -16039,7 +16170,7 @@ }, "requestretry": { "version": "1.5.0", - "resolved": "http://registry.npmjs.org/requestretry/-/requestretry-1.5.0.tgz", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.5.0.tgz", "integrity": "sha1-7RV7ulNSbt6z7DKo5wSkmYvs5ic=", "dev": true, "requires": { @@ -16171,7 +16302,7 @@ }, "rimraf": { "version": "2.4.5", - "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "requires": { "glob": "^6.0.1" @@ -16266,7 +16397,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" @@ -16297,7 +16428,7 @@ }, "sax": { "version": "1.2.1", - "resolved": "http://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, "scandirectory": { @@ -16311,6 +16442,15 @@ "taskgroup": "^4.0.5" } }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "schema-inspector": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/schema-inspector/-/schema-inspector-1.6.8.tgz", @@ -16321,7 +16461,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" } } @@ -16406,7 +16546,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -17148,7 +17288,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -17725,7 +17865,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -18521,7 +18661,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" } } @@ -19190,7 +19330,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", @@ -19294,7 +19434,7 @@ }, "xolvio-ddp": { "version": "0.12.3", - "resolved": "http://registry.npmjs.org/xolvio-ddp/-/xolvio-ddp-0.12.3.tgz", + "resolved": "https://registry.npmjs.org/xolvio-ddp/-/xolvio-ddp-0.12.3.tgz", "integrity": "sha1-NqarlhKyQLWg0cCoNJCK8XwLjwI=", "dev": true, "requires": { @@ -19319,7 +19459,7 @@ }, "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -19331,7 +19471,7 @@ }, "bl": { "version": "0.9.5", - "resolved": "http://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", "dev": true, "requires": { @@ -19340,7 +19480,7 @@ }, "bluebird": { "version": "2.11.0", - "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", "dev": true }, @@ -19352,7 +19492,7 @@ }, "combined-stream": { "version": "0.0.7", - "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", "dev": true, "requires": { @@ -19373,7 +19513,7 @@ }, "form-data": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", "integrity": "sha1-Jvi8JtpkQOKZy9z7aQNcT3em5GY=", "dev": true, "requires": { @@ -19413,13 +19553,13 @@ }, "mime-db": { "version": "1.12.0", - "resolved": "http://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=", "dev": true }, "mime-types": { "version": "2.0.14", - "resolved": "http://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", "dev": true, "requires": { @@ -19446,7 +19586,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -19458,7 +19598,7 @@ }, "request": { "version": "2.53.0", - "resolved": "http://registry.npmjs.org/request/-/request-2.53.0.tgz", + "resolved": "https://registry.npmjs.org/request/-/request-2.53.0.tgz", "integrity": "sha1-GAo66St7Y5gC5PlUXdj83rcddgw=", "dev": true, "requires": { @@ -19497,7 +19637,7 @@ }, "xolvio-fiber-utils": { "version": "2.0.3", - "resolved": "http://registry.npmjs.org/xolvio-fiber-utils/-/xolvio-fiber-utils-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/xolvio-fiber-utils/-/xolvio-fiber-utils-2.0.3.tgz", "integrity": "sha1-vsjXDHQGGjFjFbun0w0lyz6C3FA=", "dev": true, "requires": { @@ -19515,7 +19655,7 @@ }, "xolvio-jasmine-expect": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/xolvio-jasmine-expect/-/xolvio-jasmine-expect-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/xolvio-jasmine-expect/-/xolvio-jasmine-expect-1.1.0.tgz", "integrity": "sha1-vCud1ghCMR8EV59agtzqaisxnH0=", "dev": true, "requires": { @@ -19582,7 +19722,7 @@ }, "yargs": { "version": "3.32.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "requires": { "camelcase": "^2.0.1", diff --git a/package.json b/package.json index 67801442083..14e79a8b878 100644 --- a/package.json +++ b/package.json @@ -104,12 +104,13 @@ "chimp": "^0.51.1", "emojione-assets": "^4.5.0", "eslint": "^5.9.0", + "eslint-plugin-react": "^7.14.3", "fast-glob": "^2.2.6", "husky": "^1.2.0", "mocha": "^5.2.0", "mock-require": "^3.0.2", - "node-sprite-generator": "^0.10.2", "mongo-unit": "^1.4.4", + "node-sprite-generator": "^0.10.2", "postcss": "^7.0.6", "postcss-custom-properties": "^8.0.9", "postcss-easy-import": "^3.0.0", @@ -135,6 +136,8 @@ "@google-cloud/storage": "^2.3.1", "@google-cloud/vision": "^0.23.0", "@rocket.chat/apps-engine": "^1.5.2", + "@rocket.chat/fuselage": "^0.2.0-alpha.2", + "@rocket.chat/icons": "^0.2.0-alpha.0", "@slack/client": "^4.8.0", "adm-zip": "^0.4.13", "apollo-server-express": "^1.3.6", @@ -214,6 +217,8 @@ "prom-client": "^11.2.0", "querystring": "^0.2.0", "queue-fifo": "^0.2.5", + "react": "^16.8.6", + "react-dom": "^16.8.6", "redis": "^2.8.0", "semver": "^5.6.0", "sharp": "^0.22.1", diff --git a/public/fonts/RocketChat.eot b/public/fonts/RocketChat.eot new file mode 120000 index 00000000000..25d326d7e00 --- /dev/null +++ b/public/fonts/RocketChat.eot @@ -0,0 +1 @@ +../../node_modules/@rocket.chat/icons/dist/font/RocketChat.eot \ No newline at end of file diff --git a/public/fonts/RocketChat.svg b/public/fonts/RocketChat.svg new file mode 120000 index 00000000000..6360397c7a6 --- /dev/null +++ b/public/fonts/RocketChat.svg @@ -0,0 +1 @@ +../../node_modules/@rocket.chat/icons/dist/font/RocketChat.svg \ No newline at end of file diff --git a/public/fonts/RocketChat.ttf b/public/fonts/RocketChat.ttf new file mode 120000 index 00000000000..4a2797b73b6 --- /dev/null +++ b/public/fonts/RocketChat.ttf @@ -0,0 +1 @@ +../../node_modules/@rocket.chat/icons/dist/font/RocketChat.ttf \ No newline at end of file diff --git a/public/fonts/RocketChat.woff b/public/fonts/RocketChat.woff new file mode 120000 index 00000000000..5530527154c --- /dev/null +++ b/public/fonts/RocketChat.woff @@ -0,0 +1 @@ +../../node_modules/@rocket.chat/icons/dist/font/RocketChat.woff \ No newline at end of file diff --git a/public/fonts/RocketChat.woff2 b/public/fonts/RocketChat.woff2 new file mode 120000 index 00000000000..85ba5007685 --- /dev/null +++ b/public/fonts/RocketChat.woff2 @@ -0,0 +1 @@ +../../node_modules/@rocket.chat/icons/dist/font/RocketChat.woff2 \ No newline at end of file diff --git a/server/importPackages.js b/server/importPackages.js index 2c5b7ad7430..a8981a90f60 100644 --- a/server/importPackages.js +++ b/server/importPackages.js @@ -61,7 +61,6 @@ import '../app/otr/server'; import '../app/push-notifications/server'; import '../app/retention-policy'; import '../app/apps/server'; -import '../app/setup-wizard/server'; import '../app/slackbridge/server'; import '../app/slashcommands-archiveroom/server'; import '../app/slashcommand-asciiarts/server'; diff --git a/server/main.js b/server/main.js index 2e682195f09..2d7e2e97270 100644 --- a/server/main.js +++ b/server/main.js @@ -30,6 +30,7 @@ import './methods/getAvatarSuggestion'; import './methods/getRoomById'; import './methods/getRoomIdByNameOrId'; import './methods/getRoomNameById'; +import './methods/getSetupWizardParameters'; import './methods/getTotalChannels'; import './methods/getUsersOfRoom'; import './methods/hideRoom'; diff --git a/app/setup-wizard/server/getSetupWizardParameters.js b/server/methods/getSetupWizardParameters.js similarity index 52% rename from app/setup-wizard/server/getSetupWizardParameters.js rename to server/methods/getSetupWizardParameters.js index b2a178e4be3..22e018e8765 100644 --- a/app/setup-wizard/server/getSetupWizardParameters.js +++ b/server/methods/getSetupWizardParameters.js @@ -1,17 +1,9 @@ import { Meteor } from 'meteor/meteor'; -import { hasRole } from '../../authorization'; -import { Settings } from '../../models'; +import { Settings } from '../../app/models'; Meteor.methods({ getSetupWizardParameters() { - const userId = Meteor.userId(); - const userHasAdminRole = userId && hasRole(userId, 'admin'); - - if (!userHasAdminRole) { - throw new Meteor.Error('error-not-allowed'); - } - const settings = Settings.findSetupWizardSettings().fetch(); const allowStandaloneServer = process.env.DEPLOY_PLATFORM !== 'rocket-cloud'; diff --git a/tests/pageobjects/setup-wizard.page.js b/tests/pageobjects/setup-wizard.page.js index 296f178bfc2..5d791b023f7 100644 --- a/tests/pageobjects/setup-wizard.page.js +++ b/tests/pageobjects/setup-wizard.page.js @@ -3,9 +3,9 @@ import { adminEmail, adminPassword } from '../data/user'; class SetupWizard extends Page { - get nextButton() { return browser.element('.setup-wizard-forms__footer-next:enabled'); } + get nextButton() { return browser.element('.SetupWizard .SetupWizard__Step--active .SetupWizard__continue'); } - get goToWorkspace() { return browser.element('button.js-finish'); } + get goToWorkspace() { return browser.element('.SetupWizard .SetupWizard__Epilogue__goToWorkspace'); } get organizationType() { return browser.element('select[name="Organization_Type"]'); }