From ef4aa3c3fdbcb6911991649aeee2cbbdafe506c0 Mon Sep 17 00:00:00 2001 From: Sven Efftinge Date: Mon, 29 Mar 2021 11:04:43 +0000 Subject: [PATCH] [dashboard] What's New --- components/dashboard/README.md | 2 +- components/dashboard/src/App.tsx | 89 +++++++++++-------- components/dashboard/src/WhatsNew.tsx | 75 ++++++++++++++++ .../dashboard/src/start/CreateWorkspace.tsx | 2 +- components/gitpod-protocol/src/protocol.ts | 2 + 5 files changed, 129 insertions(+), 41 deletions(-) create mode 100644 components/dashboard/src/WhatsNew.tsx diff --git a/components/dashboard/README.md b/components/dashboard/README.md index aad6079c3bda1b..42ac68ab382ffb 100644 --- a/components/dashboard/README.md +++ b/components/dashboard/README.md @@ -2,7 +2,7 @@ The dashboard is written in TypeScript and React. For styling it uses TailwindCSS which is a bit nicer than inlining CSS as it supports pseudo classes and a is a little more abstract/reusable. -The App.tsx is the entry point for the SPA and it uses React-Router to register all pages. +The `App.tsx` is the entry point for the SPA and it uses React-Router to register all pages. ```ts diff --git a/components/dashboard/src/App.tsx b/components/dashboard/src/App.tsx index 88b68ed9ea765f..b6d260843bb4d5 100644 --- a/components/dashboard/src/App.tsx +++ b/components/dashboard/src/App.tsx @@ -9,12 +9,11 @@ import Menu from './components/Menu'; import { BrowserRouter } from "react-router-dom"; import { Route, Switch } from "react-router"; import { Workspaces } from './workspaces/Workspaces'; -import { CreateWorkspace } from './start/CreateWorkspace'; -import StartWorkspace from './start/StartWorkspace'; + import { Login } from './Login'; import { UserContext } from './user-context'; import { getGitpodService } from './service/service'; -import Header from './components/Header'; +import { shouldSeeWhatsNew, WhatsNew } from './WhatsNew'; const Account = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Account')); const Notifications = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Notifications')); @@ -22,10 +21,11 @@ const Plans = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Pl const EnvironmentVariables = React.lazy(() => import(/* webpackPrefetch: true */ './settings/EnvironmentVariables')); const Integrations = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Integrations')); const Preferences = React.lazy(() => import(/* webpackPrefetch: true */ './settings/Preferences')); +const StartWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/StartWorkspace')); +const CreateWorkspace = React.lazy(() => import(/* webpackPrefetch: true */ './start/CreateWorkspace')); function Loading() { return <> -
; } @@ -33,65 +33,76 @@ function App() { const { user, setUser } = useContext(UserContext); const [loading, setLoading] = useState(true); + const [isWhatsNewShown, setWhatsNewShown] = useState(false); useEffect(() => { (async () => { try { - setUser(await getGitpodService().server.getLoggedInUser()); + const usr = await getGitpodService().server.getLoggedInUser() + setUser(usr); } catch (error) { console.log(error); } setLoading(false); })(); }, []); - - if (!loading && !user) { + + if (loading) { + return + } + if (!user) { return () }; - + const shouldWhatsNewShown = shouldSeeWhatsNew(user) + if (shouldWhatsNewShown !== isWhatsNewShown) { + setWhatsNewShown(shouldWhatsNewShown); + } + window.addEventListener("hashchange", () => { - // Refresh on hash change if the path is '/' (new context URL) - if (window.location.pathname === '/') { - window.location.reload(true); - } + // Refresh on hash change if the path is '/' (new context URL) + if (window.location.pathname === '/') { + window.location.reload(true); + } }, false); + let toRender: React.ReactElement = +
+ {renderMenu()} + + } /> + + + + + + + +
+
; + const hash = getURLHash(); - if (window.location.pathname === '/' && hash !== '') { - return ; - } - if (/\/start\/?/.test(window.location.pathname) && hash !== '') { - return ; + const isCreation = window.location.pathname === '/' && hash !== ''; + const isWsStart = /\/start\/?/.test(window.location.pathname) && hash !== ''; + if (isWhatsNewShown) { + toRender = setWhatsNewShown(false)} />; + } else if (isCreation) { + toRender = ; + } else if (isWsStart) { + ; } return ( -
- {user && renderMenu()} - - }> - - {user && ( - - } /> - - - - - - - - )} - - -
+ }> + {toRender} +
); } -function getURLHash () { - return window.location.hash.replace(/^[#/]+/, ''); +function getURLHash() { + return window.location.hash.replace(/^[#/]+/, ''); } const renderMenu = () => ( diff --git a/components/dashboard/src/WhatsNew.tsx b/components/dashboard/src/WhatsNew.tsx new file mode 100644 index 00000000000000..4f40f5fc19c4b2 --- /dev/null +++ b/components/dashboard/src/WhatsNew.tsx @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2021 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +import { User } from "@gitpod/gitpod-protocol"; +import { useContext } from "react"; +import Modal from "./components/Modal"; +import { getGitpodService } from "./service/service"; +import { UserContext } from "./user-context"; + +const news = 'April-2021'; +export function shouldSeeWhatsNew(user: User): boolean { + const whatsNewSeen = user?.additionalData?.whatsNewSeen; + return user.creationDate <= '2021-04-08' && (!whatsNewSeen || !whatsNewSeen[news]); +} + +export function WhatsNew(props: { visible: boolean, onClose: () => void }) { + const {user, setUser} = useContext(UserContext); + const internalClose = async (switchToCode?: boolean) => { + if (!user) { + return; + } + const additionalData = user.additionalData || {}; + additionalData.whatsNewSeen = { + ...additionalData.whatsNewSeen, + [news]: new Date().toISOString() + } + if (switchToCode) { + const ideSettings = additionalData.ideSettings || {}; + ideSettings.defaultIde = 'code'; + } + await getGitpodService().server.updateLoggedInUser({ + additionalData + }); + setUser(user); + props.onClose(); + } + return +

What's New 🎁

+
+

New Dashboard

+

We have made some layout changes on the dashboard to improve the overall user experience of the product.

+
+
+

VS Code

+

We are changing the default IDE to VS Code.

+
    +
  1. +
    +

    We're preserving all user settings and extensions.

    +

    Extensions you have manually uploaded are not transfered. You'll need to search and install those extensions through the extension panel in VS Code.

    +
    +
  2. +
  3. +
    +

    We've reduced the number of pre-installed extensions.

    +

    The Theia-based editor included pre-installed extensions for the most popular programming languages which was convenienvt for starters but added additional bloat. You can now install any extensions you need and leave out those you don't.

    +
    +
  4. +
  5. +
    +

    You can still switch the editor to Theia.

    +

    In case you run into trouble with the new IDE, you can go to the settings and switch back to the Theia-based editor.

    +
    +
  6. +
+
+
+ + +
+
+} \ No newline at end of file diff --git a/components/dashboard/src/start/CreateWorkspace.tsx b/components/dashboard/src/start/CreateWorkspace.tsx index 674eceefa54dac..d3943bd45f3c2f 100644 --- a/components/dashboard/src/start/CreateWorkspace.tsx +++ b/components/dashboard/src/start/CreateWorkspace.tsx @@ -31,7 +31,7 @@ export interface CreateWorkspaceError { data?: any; } -export class CreateWorkspace extends React.Component { +export default class CreateWorkspace extends React.Component { constructor(props: CreateWorkspaceProps) { super(props); diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index eb47688f5125e2..089f3510b85885 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -98,6 +98,8 @@ export interface AdditionalUserData { emailNotificationSettings?: EmailNotificationSettings; featurePreview?: boolean; ideSettings?: IDESettings; + // key is the name of the news, string the iso date when it was seen + whatsNewSeen?: { [key: string]: string } } export interface EmailNotificationSettings {