From 7be7bed91696eadf79817e553207eb4d8cd3579a Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 30 Mar 2021 15:25:16 -0700 Subject: [PATCH 1/2] Show workspace title in sidebar --- server/api/api.go | 8 ++++++-- server/einterfaces/einterfaces.go | 1 + server/model/workspace.go | 4 ++++ webapp/src/blocks/workspace.ts | 1 + webapp/src/components/sidebar/sidebar.scss | 5 +++++ webapp/src/components/sidebar/sidebar.tsx | 15 +++++++++++---- webapp/src/components/workspace.tsx | 7 +++++-- webapp/src/pages/boardPage.tsx | 11 +++++++---- 8 files changed, 40 insertions(+), 12 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index b24748ae7..fd04b409f 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -35,6 +35,7 @@ const ( type WorkspaceAuthenticator interface { DoesUserHaveWorkspaceAccess(session *model.Session, workspaceID string) bool + GetWorkspace(session *model.Session, workspaceID string) *model.Workspace } type API struct { @@ -902,8 +903,11 @@ func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) { errorResponse(w, http.StatusUnauthorized, "", nil) return } - workspace = &model.Workspace{ - ID: workspaceID, + + workspace = a.WorkspaceAuthenticator.GetWorkspace(session, workspaceID) + if workspace == nil { + errorResponse(w, http.StatusUnauthorized, "", nil) + return } } else { workspace, err = a.app().GetRootWorkspace() diff --git a/server/einterfaces/einterfaces.go b/server/einterfaces/einterfaces.go index 9c45fcbc0..385db9835 100644 --- a/server/einterfaces/einterfaces.go +++ b/server/einterfaces/einterfaces.go @@ -8,6 +8,7 @@ import ( type MattermostAuth interface { RegisterRoutes(*mux.Router) DoesUserHaveWorkspaceAccess(session *model.Session, workspaceID string) bool + GetWorkspace(session *model.Session, workspaceID string) *model.Workspace } type MattermostAuthParameters struct { diff --git a/server/model/workspace.go b/server/model/workspace.go index a43537da3..f302fcd61 100644 --- a/server/model/workspace.go +++ b/server/model/workspace.go @@ -7,6 +7,10 @@ type Workspace struct { // required: true ID string `json:"id"` + // Title of the workspace + // required: false + Title string `json:"title"` + // Token required to register new users // required: true SignupToken string `json:"signupToken"` diff --git a/webapp/src/blocks/workspace.ts b/webapp/src/blocks/workspace.ts index 478d86f9e..3a7112f81 100644 --- a/webapp/src/blocks/workspace.ts +++ b/webapp/src/blocks/workspace.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. interface IWorkspace { readonly id: string, + readonly title: string, readonly signupToken: string, readonly settings: Readonly> readonly modifiedBy?: string, diff --git a/webapp/src/components/sidebar/sidebar.scss b/webapp/src/components/sidebar/sidebar.scss index 2d55b0435..d81576f78 100644 --- a/webapp/src/components/sidebar/sidebar.scss +++ b/webapp/src/components/sidebar/sidebar.scss @@ -39,6 +39,11 @@ } } + .WorkspaceTitle { + padding: 0 16px; + font-weight: 600; + } + .octo-sidebar-list { flex: 1 1 auto; overflow-y: auto; diff --git a/webapp/src/components/sidebar/sidebar.tsx b/webapp/src/components/sidebar/sidebar.tsx index 09c1ed05e..5ef6cc0f5 100644 --- a/webapp/src/components/sidebar/sidebar.tsx +++ b/webapp/src/components/sidebar/sidebar.tsx @@ -1,7 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useState, useEffect} from 'react' +import React, {useEffect, useState} from 'react' +import {IWorkspace} from '../../blocks/workspace' import {loadTheme} from '../../theme' import {WorkspaceTree} from '../../viewModel/workspaceTree' import IconButton from '../../widgets/buttons/iconButton' @@ -9,13 +10,14 @@ import HamburgerIcon from '../../widgets/icons/hamburger' import HideSidebarIcon from '../../widgets/icons/hideSidebar' import ShowSidebarIcon from '../../widgets/icons/showSidebar' -import SidebarSettingsMenu from './sidebarSettingsMenu' +import './sidebar.scss' import SidebarAddBoardMenu from './sidebarAddBoardMenu' import SidebarBoardItem from './sidebarBoardItem' +import SidebarSettingsMenu from './sidebarSettingsMenu' import SidebarUserMenu from './sidebarUserMenu' -import './sidebar.scss' type Props = { + workspace?: IWorkspace showBoard: (id?: string) => void showView: (id: string, boardId?: string) => void workspaceTree: WorkspaceTree, @@ -35,7 +37,7 @@ const Sidebar = React.memo((props: Props) => { } }, []) - const {workspaceTree} = props + const {workspace, workspaceTree} = props if (!workspaceTree) { return
} @@ -76,6 +78,11 @@ const Sidebar = React.memo((props: Props) => { icon={} />
+ {workspace && workspace.id !== '0' && +
+ {workspace.title} +
+ }
{ boards.map((board) => { diff --git a/webapp/src/components/workspace.tsx b/webapp/src/components/workspace.tsx index 9cbe782de..4d2281601 100644 --- a/webapp/src/components/workspace.tsx +++ b/webapp/src/components/workspace.tsx @@ -3,15 +3,17 @@ import React from 'react' import {FormattedMessage} from 'react-intl' +import {IWorkspace} from '../blocks/workspace' import {Utils} from '../utils' import {BoardTree} from '../viewModel/boardTree' import {WorkspaceTree} from '../viewModel/workspaceTree' -import Sidebar from './sidebar/sidebar' import CenterPanel from './centerPanel' +import Sidebar from './sidebar/sidebar' import './workspace.scss' type Props = { + workspace?: IWorkspace workspaceTree: WorkspaceTree boardTree?: BoardTree showBoard: (id?: string) => void @@ -22,7 +24,7 @@ type Props = { } const Workspace = React.memo((props: Props) => { - const {boardTree, setSearchText, workspaceTree, showBoard, showView, setLanguage} = props + const {workspace, boardTree, setSearchText, workspaceTree, showBoard, showView, setLanguage} = props const {activeView} = boardTree || {} Utils.assert(workspaceTree || !props.readonly) @@ -31,6 +33,7 @@ const Workspace = React.memo((props: Props) => {
{!props.readonly && { render(): JSX.Element { const {intl} = this.props - const {workspaceTree} = this.state + const {workspace, workspaceTree} = this.state Utils.log(`BoardPage.render (workspace ${this.props.workspaceId}) ${this.state.boardTree?.board?.title}`) @@ -164,6 +166,7 @@ class BoardPage extends React.Component { return (
{ @@ -202,10 +205,10 @@ class BoardPage extends React.Component { private async sync(boardId: string = this.state.boardId, viewId: string | undefined = this.state.viewId) { Utils.log(`sync start: ${boardId}`) + let workspace: IWorkspace | undefined if (!this.props.readonly) { // Require workspace for editing, not for sharing (readonly) - - const workspace = await octoClient.getWorkspace() + workspace = await octoClient.getWorkspace() if (!workspace) { location.href = '/error?id=no_workspace' } @@ -213,7 +216,7 @@ class BoardPage extends React.Component { const workspaceTree = await MutableWorkspaceTree.sync() const boardIds = [...workspaceTree.boards.map((o) => o.id), ...workspaceTree.boardTemplates.map((o) => o.id)] - this.setState({workspaceTree}) + this.setState({workspace, workspaceTree}) let boardIdsToListen: string[] if (boardIds.length > 0) { From 884b6580106c1a40fd8866890a448ae2792d834c Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 30 Mar 2021 15:50:37 -0700 Subject: [PATCH 2/2] Show no board hint with workspace title --- webapp/i18n/en.json | 2 ++ webapp/src/components/emptyCenterPanel.scss | 14 ++++++++ webapp/src/components/emptyCenterPanel.tsx | 40 +++++++++++++++++++++ webapp/src/components/workspace.tsx | 32 ++++++++++++----- 4 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 webapp/src/components/emptyCenterPanel.scss create mode 100644 webapp/src/components/emptyCenterPanel.tsx diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 094967d6e..1e8e4d535 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -31,6 +31,8 @@ "ContentBlock.moveDown": "Move down", "ContentBlock.moveUp": "Move up", "ContentBlock.text": "text", + "EmptyCenterPanel.no-content": "Add or select a board from the sidebar to get started.", + "EmptyCenterPanel.workspace": "This is the workspace for:", "Filter.includes": "includes", "Filter.is-empty": "is empty", "Filter.is-not-empty": "is not empty", diff --git a/webapp/src/components/emptyCenterPanel.scss b/webapp/src/components/emptyCenterPanel.scss new file mode 100644 index 000000000..cc41b39f2 --- /dev/null +++ b/webapp/src/components/emptyCenterPanel.scss @@ -0,0 +1,14 @@ +.EmptyCenterPanel { + display: flex; + flex-direction: column; + + padding: 80px; + font-size: 15px; + color: rgba(var(--body-color), 0.7); + + .WorkspaceInfo { + b { + padding-left: 5px; + } + } +} diff --git a/webapp/src/components/emptyCenterPanel.tsx b/webapp/src/components/emptyCenterPanel.tsx new file mode 100644 index 000000000..351910f92 --- /dev/null +++ b/webapp/src/components/emptyCenterPanel.tsx @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react' +import {FormattedMessage, injectIntl, IntlShape} from 'react-intl' + +import {IWorkspace} from '../blocks/workspace' +import './emptyCenterPanel.scss' + +type Props = { + workspace?: IWorkspace + intl: IntlShape +} + +const EmptyCenterPanel = React.memo((props: Props) => { + const {workspace} = props + + return ( +
+ {workspace && workspace.id !== '0' && +
+ + + {workspace.title} + +
+ } +
+ +
+
+ ) +}) + +export default injectIntl(EmptyCenterPanel) diff --git a/webapp/src/components/workspace.tsx b/webapp/src/components/workspace.tsx index 4d2281601..ce6fa49f2 100644 --- a/webapp/src/components/workspace.tsx +++ b/webapp/src/components/workspace.tsx @@ -9,6 +9,7 @@ import {BoardTree} from '../viewModel/boardTree' import {WorkspaceTree} from '../viewModel/workspaceTree' import CenterPanel from './centerPanel' +import EmptyCenterPanel from './emptyCenterPanel' import Sidebar from './sidebar/sidebar' import './workspace.scss' @@ -23,10 +24,29 @@ type Props = { readonly: boolean } -const Workspace = React.memo((props: Props) => { - const {workspace, boardTree, setSearchText, workspaceTree, showBoard, showView, setLanguage} = props +function centerContent(props: Props) { + const {workspace, boardTree, setSearchText, showView} = props const {activeView} = boardTree || {} + if (boardTree && activeView) { + return ( + + ) + } + + return ( + + ) +} + +const Workspace = React.memo((props: Props) => { + const {workspace, boardTree, workspaceTree, showBoard, showView, setLanguage} = props + Utils.assert(workspaceTree || !props.readonly) return ( @@ -49,13 +69,7 @@ const Workspace = React.memo((props: Props) => { defaultMessage="You're editing a board template" />
} - {boardTree && activeView && - } + {centerContent(props)}
)