Merge pull request #218 from mattermost/workspace-title

Workspace title
This commit is contained in:
Chen-I Lim 2021-03-31 07:59:40 -07:00 committed by GitHub
commit 18a240c74c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 118 additions and 20 deletions

View File

@ -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()

View File

@ -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 {

View File

@ -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"`

View File

@ -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",

View File

@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
interface IWorkspace {
readonly id: string,
readonly title: string,
readonly signupToken: string,
readonly settings: Readonly<Record<string, any>>
readonly modifiedBy?: string,

View File

@ -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;
}
}
}

View File

@ -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 (
<div className='EmptyCenterPanel'>
{workspace && workspace.id !== '0' &&
<div className='WorkspaceInfo'>
<FormattedMessage
id='EmptyCenterPanel.workspace'
defaultMessage='This is the workspace for:'
/>
<b>
{workspace.title}
</b>
</div>
}
<div className='Hint'>
<FormattedMessage
id='EmptyCenterPanel.no-content'
defaultMessage='Add or select a board from the sidebar to get started.'
/>
</div>
</div>
)
})
export default injectIntl(EmptyCenterPanel)

View File

@ -39,6 +39,11 @@
}
}
.WorkspaceTitle {
padding: 0 16px;
font-weight: 600;
}
.octo-sidebar-list {
flex: 1 1 auto;
overflow-y: auto;

View File

@ -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 <div/>
}
@ -76,6 +78,11 @@ const Sidebar = React.memo((props: Props) => {
icon={<HideSidebarIcon/>}
/>
</div>
{workspace && workspace.id !== '0' &&
<div className='WorkspaceTitle'>
{workspace.title}
</div>
}
<div className='octo-sidebar-list'>
{
boards.map((board) => {

View File

@ -3,15 +3,18 @@
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 EmptyCenterPanel from './emptyCenterPanel'
import Sidebar from './sidebar/sidebar'
import './workspace.scss'
type Props = {
workspace?: IWorkspace
workspaceTree: WorkspaceTree
boardTree?: BoardTree
showBoard: (id?: string) => void
@ -21,16 +24,36 @@ type Props = {
readonly: boolean
}
const Workspace = React.memo((props: Props) => {
const {boardTree, setSearchText, workspaceTree, showBoard, showView, setLanguage} = props
function centerContent(props: Props) {
const {workspace, boardTree, setSearchText, showView} = props
const {activeView} = boardTree || {}
if (boardTree && activeView) {
return (
<CenterPanel
boardTree={boardTree}
setSearchText={setSearchText}
showView={showView}
readonly={props.readonly}
/>
)
}
return (
<EmptyCenterPanel workspace={workspace}/>
)
}
const Workspace = React.memo((props: Props) => {
const {workspace, boardTree, workspaceTree, showBoard, showView, setLanguage} = props
Utils.assert(workspaceTree || !props.readonly)
return (
<div className='Workspace'>
{!props.readonly &&
<Sidebar
workspace={workspace}
showBoard={showBoard}
showView={showView}
workspaceTree={workspaceTree}
@ -46,13 +69,7 @@ const Workspace = React.memo((props: Props) => {
defaultMessage="You're editing a board template"
/>
</div>}
{boardTree && activeView &&
<CenterPanel
boardTree={boardTree}
setSearchText={setSearchText}
showView={showView}
readonly={props.readonly}
/>}
{centerContent(props)}
</div>
</div>
)

View File

@ -4,6 +4,7 @@ import React from 'react'
import {injectIntl, IntlShape} from 'react-intl'
import {IBlock} from '../blocks/block'
import {IWorkspace} from '../blocks/workspace'
import {sendFlashMessage} from '../components/flashMessages'
import Workspace from '../components/workspace'
import mutator from '../mutator'
@ -24,6 +25,7 @@ type Props = {
type State = {
boardId: string
viewId: string
workspace?: IWorkspace,
workspaceTree: WorkspaceTree
boardTree?: BoardTree
syncFailed?: boolean
@ -143,7 +145,7 @@ class BoardPage extends React.Component<Props, State> {
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<Props, State> {
return (
<div className='BoardPage'>
<Workspace
workspace={workspace}
workspaceTree={workspaceTree}
boardTree={this.state.boardTree}
showView={(id, boardId) => {
@ -202,10 +205,10 @@ class BoardPage extends React.Component<Props, State> {
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<Props, State> {
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) {