Просмотр исходного кода

fixed routing and initialization data loading for sidebar

Skylsmoi 6 лет назад
Родитель
Сommit
0713817e8e

+ 1 - 1
frontend/src/component/Dashboard/RecentActivity.jsx Просмотреть файл

@@ -21,7 +21,7 @@ export const RecentActivity = props =>
21 21
 
22 22
     <div className='activity__wrapper'>
23 23
       {props.recentActivityList.map(content => {
24
-        const contentType = props.contentTypeList.find(ct => ct.slug === content.type)
24
+        const contentType = props.contentTypeList.find(ct => ct.slug === content.type) || {hexcolor: '', faIcon: ''}
25 25
         return (
26 26
           <div
27 27
             className={classnames('activity__workspace', {'read': props.readByUserList.includes(content.id)})}

+ 5 - 10
frontend/src/container/Account.jsx Просмотреть файл

@@ -29,18 +29,15 @@ class Account extends React.Component {
29 29
         name: 'personalData',
30 30
         menuLabel: 'Mon profil',
31 31
         active: true
32
-      },
33
-      {
32
+      }, {
34 33
         name: 'notification',
35 34
         menuLabel: 'Espace de travail & Notifications',
36 35
         active: false
37
-      },
38
-      {
36
+      }, {
39 37
         name: 'password',
40 38
         menuLabel: 'Mot de passe',
41 39
         active: false
42
-      },
43
-      {
40
+      }, {
44 41
         name: 'timezone',
45 42
         menuLabel: 'Fuseau Horaire',
46 43
         active: false
@@ -79,8 +76,7 @@ class Account extends React.Component {
79 76
     const subComponent = (() => {
80 77
       switch (this.state.subComponentMenu.find(({active}) => active).name) {
81 78
         case 'personalData':
82
-          return <PersonalData
83
-          />
79
+          return <PersonalData />
84 80
 
85 81
         // case 'calendar':
86 82
         //   return <Calendar user={this.props.user} />
@@ -92,8 +88,7 @@ class Account extends React.Component {
92 88
           />
93 89
 
94 90
         case 'password':
95
-          return <Password
96
-          />
91
+          return <Password />
97 92
 
98 93
         case 'timezone':
99 94
           return <Timezone timezone={this.props.timezone} onChangeTimezone={this.handleChangeTimezone} />

+ 51 - 6
frontend/src/container/Login.jsx Просмотреть файл

@@ -1,11 +1,10 @@
1 1
 import React from 'react'
2 2
 import { connect } from 'react-redux'
3
-import { Redirect } from 'react-router'
3
+import { withRouter, Redirect } from 'react-router'
4 4
 import { translate } from 'react-i18next'
5 5
 import Cookies from 'js-cookie'
6 6
 import LoginLogo from '../component/Login/LoginLogo.jsx'
7 7
 import LoginLogoImg from '../img/logoTracimWhite.svg'
8
-import { postUserLogin } from '../action-creator.async.js'
9 8
 import Card from '../component/common/Card/Card.jsx'
10 9
 import CardHeader from '../component/common/Card/CardHeader.jsx'
11 10
 import CardBody from '../component/common/Card/CardBody.jsx'
@@ -14,8 +13,18 @@ import Button from '../component/common/Input/Button.jsx'
14 13
 import LoginBtnForgotPw from '../component/Login/LoginBtnForgotPw.jsx'
15 14
 import {
16 15
   newFlashMessage,
17
-  setUserConnected
16
+  setUserConnected,
17
+  updateWorkspaceListData,
18
+  setWorkspaceListIsOpenInSidebar,
19
+  setContentTypeList,
20
+  setAppList
18 21
 } from '../action-creator.sync.js'
22
+import {
23
+  getAppList,
24
+  getContentTypeList,
25
+  getWorkspaceList,
26
+  postUserLogin
27
+} from '../action-creator.async.js'
19 28
 import { COOKIE, PAGE } from '../helper.js'
20 29
 import { Checkbox } from 'tracim_frontend_lib'
21 30
 
@@ -51,11 +60,16 @@ class Login extends React.Component {
51 60
     const userAuth = btoa(`${inputLogin.value}:${inputPassword.value}`)
52 61
 
53 62
     if (fetchPostUserLogin.status === 200) {
54
-      dispatch(setUserConnected({
63
+      const loggedUser = {
55 64
         ...fetchPostUserLogin.json,
56 65
         auth: userAuth,
57 66
         logged: true
58
-      }))
67
+      }
68
+
69
+      dispatch(setUserConnected(loggedUser))
70
+
71
+      this.loadAppConfig(loggedUser)
72
+      this.loadWorkspaceList(loggedUser)
59 73
 
60 74
       if (inputRememberMe) {
61 75
         Cookies.set(COOKIE.USER_LOGIN, inputLogin.value, {expires: 365})
@@ -71,6 +85,36 @@ class Login extends React.Component {
71 85
     }
72 86
   }
73 87
 
88
+  // @FIXME Côme - 2018/08/22 - this function is duplicated from Tracim.jsx
89
+  loadAppConfig = async user => {
90
+    const { props } = this
91
+
92
+    const fetchGetAppList = await props.dispatch(getAppList(user))
93
+    if (fetchGetAppList.status === 200) props.dispatch(setAppList(fetchGetAppList.json))
94
+
95
+    const fetchGetContentTypeList = await props.dispatch(getContentTypeList(user))
96
+    if (fetchGetContentTypeList.status === 200) props.dispatch(setContentTypeList(fetchGetContentTypeList.json))
97
+  }
98
+
99
+  // @FIXME Côme - 2018/08/22 - this function is duplicated from Tracim.jsx
100
+  loadWorkspaceList = async user => {
101
+    const { props } = this
102
+
103
+    const fetchGetWorkspaceList = await props.dispatch(getWorkspaceList(user))
104
+
105
+    if (fetchGetWorkspaceList.status === 200) {
106
+      props.dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))
107
+
108
+      const idWorkspaceToOpen = (() =>
109
+        props.match && props.match.params.idws !== undefined && !isNaN(props.match.params.idws)
110
+          ? parseInt(props.match.params.idws)
111
+          : fetchGetWorkspaceList.json[0].workspace_id
112
+      )()
113
+
114
+      props.dispatch(setWorkspaceListIsOpenInSidebar(idWorkspaceToOpen, true))
115
+    }
116
+  }
117
+
74 118
   render () {
75 119
     if (this.props.user.logged) return <Redirect to={{pathname: '/'}} />
76 120
     else {
@@ -118,6 +162,7 @@ class Login extends React.Component {
118 162
                             <Checkbox
119 163
                               name='inputRememberMe'
120 164
                               checked={this.state.inputRememberMe}
165
+                              onClickCheckbox={() => {}}
121 166
                             />
122 167
                             Se souvenir de moi
123 168
                           </div>
@@ -161,4 +206,4 @@ class Login extends React.Component {
161 206
 }
162 207
 
163 208
 const mapStateToProps = ({ user }) => ({ user })
164
-export default connect(mapStateToProps)(translate()(Login))
209
+export default withRouter(connect(mapStateToProps)(translate()(Login)))

+ 15 - 58
frontend/src/container/Sidebar.jsx Просмотреть файл

@@ -6,16 +6,8 @@ import { translate } from 'react-i18next'
6 6
 import appFactory from '../appFactory.js'
7 7
 import WorkspaceListItem from '../component/Sidebar/WorkspaceListItem.jsx'
8 8
 import {
9
-  setAppList,
10
-  setContentTypeList,
11
-  setWorkspaceListIsOpenInSidebar,
12
-  updateWorkspaceFilter,
13
-  updateWorkspaceListData
9
+  setWorkspaceListIsOpenInSidebar
14 10
 } from '../action-creator.sync.js'
15
-import {
16
-  getAppList, getContentTypeList,
17
-  getWorkspaceList
18
-} from '../action-creator.async.js'
19 11
 import { PAGE, workspaceConfig } from '../helper.js'
20 12
 
21 13
 const qs = require('query-string')
@@ -25,6 +17,7 @@ class Sidebar extends React.Component {
25 17
     super(props)
26 18
     this.state = {
27 19
       sidebarClose: false,
20
+      workspaceListLoaded: false,
28 21
       workspaceIdInUrl: props.match && props.match.params.idws ? parseInt(props.match.params.idws) : null
29 22
     }
30 23
 
@@ -40,62 +33,26 @@ class Sidebar extends React.Component {
40 33
     }
41 34
   }
42 35
 
43
-  componentDidMount () {
44
-    // console.log('Sidebar Did Mount', this.props)
45
-    this.loadAppConfigAndWorkspaceList()
46
-  }
47
-
48 36
   componentDidUpdate (prevProps, prevState) {
49 37
     const { props } = this
50 38
 
51
-    // console.log('%c<Sidebar> Did Update', 'color: #c17838')
52
-    if (!props.match || props.match.params.idws === undefined || isNaN(props.match.params.idws)) return
53
-
54
-    const newWorkspaceId = parseInt(props.match.params.idws)
55
-    if (prevState.workspaceIdInUrl !== newWorkspaceId) this.setState({workspaceIdInUrl: newWorkspaceId})
56
-  }
57
-
58
-  loadAppConfigAndWorkspaceList = async () => {
59
-    const { workspaceIdInUrl } = this.state
60
-    const { user, dispatch } = this.props
61
-
62
-    if (user.user_id !== -1) {
63
-      const fetchGetAppList = await dispatch(getAppList(user))
64
-      if (fetchGetAppList.status === 200) dispatch(setAppList(fetchGetAppList.json))
65
-
66
-      const fetchGetContentTypeList = await dispatch(getContentTypeList(user))
67
-      if (fetchGetContentTypeList.status === 200) dispatch(setContentTypeList(fetchGetContentTypeList.json))
68
-
69
-      const fetchGetWorkspaceList = await dispatch(getWorkspaceList(user))
39
+    if (!this.shouldDisplaySidebar()) return
70 40
 
71
-      if (fetchGetWorkspaceList.status === 200) {
72
-        dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))
73
-        dispatch(setWorkspaceListIsOpenInSidebar(workspaceIdInUrl || fetchGetWorkspaceList.json[0].workspace_id, true))
74
-      }
41
+    // console.log('%c<Sidebar> Did Update', 'color: #c17838')
42
+    if (props.match && props.match.params.idws !== undefined && !isNaN(props.match.params.idws)) {
43
+      const newWorkspaceId = parseInt(props.match.params.idws)
44
+      if (prevState.workspaceIdInUrl !== newWorkspaceId) this.setState({workspaceIdInUrl: newWorkspaceId})
75 45
     }
76 46
   }
77 47
 
78
-  handleClickWorkspace = (idWs, newIsOpenInSidebar) => this.props.dispatch(setWorkspaceListIsOpenInSidebar(idWs, newIsOpenInSidebar))
79
-
80
-  handleClickAllContent = idWs => {
81
-    this.props.dispatch(updateWorkspaceFilter([]))
82
-
83
-    this.props.history.push(PAGE.WORKSPACE.CONTENT_LIST(idWs))
48
+  shouldDisplaySidebar = () => {
49
+    const pageWithoutSidebar = [PAGE.LOGIN]
50
+    return !pageWithoutSidebar.includes(this.props.location.pathname)
84 51
   }
85 52
 
86
-  // @DEPRECATED
87
-  // not used, right now, link on sidebar filters is a <Link>
88
-  handleClickContentFilter = (idWs, filter) => {
89
-    const { workspace, history } = this.props
90
-
91
-    const newFilter = workspace.filter.includes(filter) ? [] : [filter] // use an array to allow multiple filters (NYI)
92
-
93
-    history.push(`${PAGE.WORKSPACE.CONTENT_LIST(idWs)}?type=${newFilter.join(';')}`) // workspace.filter gets updated on react redraw from match.params
53
+  handleClickWorkspace = (idWs, newIsOpenInSidebar) => this.props.dispatch(setWorkspaceListIsOpenInSidebar(idWs, newIsOpenInSidebar))
94 54
 
95
-    // obviously, it's ugly to use custom event to tell WorkspaceContentList to refresh, but since WorkspaceContentList
96
-    // will end up being an App, it'll have to be that way. So it's fine
97
-    GLOBAL_dispatchEvent({ type: 'refreshContentList', data: {} })
98
-  }
55
+  handleClickAllContent = idWs => this.props.history.push(PAGE.WORKSPACE.CONTENT_LIST(idWs))
99 56
 
100 57
   handleClickToggleSidebar = () => this.setState(prev => ({sidebarClose: !prev.sidebarClose}))
101 58
 
@@ -105,6 +62,8 @@ class Sidebar extends React.Component {
105 62
     const { sidebarClose, workspaceIdInUrl } = this.state
106 63
     const { activeLang, workspaceList, t } = this.props
107 64
 
65
+    if (!this.shouldDisplaySidebar()) return null
66
+
108 67
     return (
109 68
       <div className={classnames('sidebar primaryColorBgDarken', {'sidebarclose': sidebarClose})}>
110 69
 
@@ -125,7 +84,6 @@ class Sidebar extends React.Component {
125 84
                   isOpenInSidebar={ws.isOpenInSidebar}
126 85
                   onClickTitle={() => this.handleClickWorkspace(ws.id, !ws.isOpenInSidebar)}
127 86
                   onClickAllContent={this.handleClickAllContent}
128
-                  // onClickContentFilter={this.handleClickContentFilter}
129 87
                   key={ws.id}
130 88
                 />
131 89
               )}
@@ -156,10 +114,9 @@ class Sidebar extends React.Component {
156 114
   }
157 115
 }
158 116
 
159
-const mapStateToProps = ({ lang, user, workspace, workspaceList }) => ({
117
+const mapStateToProps = ({ lang, user, workspaceList }) => ({
160 118
   activeLang: lang.find(l => l.active) || {id: 'en'},
161 119
   user,
162
-  workspace,
163 120
   workspaceList
164 121
 })
165 122
 export default withRouter(connect(mapStateToProps)(appFactory(translate()(Sidebar))))

+ 81 - 51
frontend/src/container/Tracim.jsx Просмотреть файл

@@ -14,12 +14,16 @@ import {
14 14
 } from 'react-router-dom'
15 15
 import { COOKIE, PAGE } from '../helper.js'
16 16
 import {
17
-  getUserIsConnected
17
+  getAppList,
18
+  getContentTypeList,
19
+  getUserIsConnected, getWorkspaceList
18 20
 } from '../action-creator.async.js'
19 21
 import {
20 22
   newFlashMessage,
21 23
   removeFlashMessage,
22
-  setUserConnected
24
+  setAppList,
25
+  setContentTypeList,
26
+  setUserConnected, setWorkspaceListIsOpenInSidebar, updateWorkspaceListData
23 27
 } from '../action-creator.sync.js'
24 28
 import Cookies from 'js-cookie'
25 29
 import Dashboard from './Dashboard.jsx'
@@ -61,6 +65,8 @@ class Tracim extends React.Component {
61 65
           auth: userFromCookies.auth,
62 66
           logged: true
63 67
         }))
68
+        this.loadAppConfig()
69
+        this.loadWorkspaceList()
64 70
         break
65 71
       case 401:
66 72
         dispatch(setUserConnected({logged: false})); break
@@ -69,6 +75,36 @@ class Tracim extends React.Component {
69 75
     }
70 76
   }
71 77
 
78
+  loadAppConfig = async () => {
79
+    const { props } = this
80
+
81
+    const fetchGetAppList = await props.dispatch(getAppList(props.user))
82
+    if (fetchGetAppList.status === 200) props.dispatch(setAppList(fetchGetAppList.json))
83
+
84
+    const fetchGetContentTypeList = await props.dispatch(getContentTypeList(props.user))
85
+    if (fetchGetContentTypeList.status === 200) props.dispatch(setContentTypeList(fetchGetContentTypeList.json))
86
+  }
87
+
88
+  loadWorkspaceList = async () => {
89
+    const { props } = this
90
+
91
+    const fetchGetWorkspaceList = await props.dispatch(getWorkspaceList(props.user))
92
+
93
+    if (fetchGetWorkspaceList.status === 200) {
94
+      this.setState({workspaceListLoaded: true})
95
+
96
+      props.dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))
97
+
98
+      const idWorkspaceToOpen = (() =>
99
+        props.match && props.match.params.idws !== undefined && !isNaN(props.match.params.idws)
100
+          ? parseInt(props.match.params.idws)
101
+          : fetchGetWorkspaceList.json[0].workspace_id
102
+      )()
103
+
104
+      props.dispatch(setWorkspaceListIsOpenInSidebar(idWorkspaceToOpen, true))
105
+    }
106
+  }
107
+
72 108
   handleRemoveFlashMessage = msg => this.props.dispatch(removeFlashMessage(msg))
73 109
 
74 110
   render () {
@@ -85,61 +121,55 @@ class Tracim extends React.Component {
85 121
         <Header />
86 122
         <FlashMessage flashMessage={props.flashMessage} removeFlashMessage={this.handleRemoveFlashMessage} t={props.t} />
87 123
 
88
-        <div className='tracim__content'>
89
-          <Route path={PAGE.LOGIN} component={Login} />
90
-
91
-          <Route exact path='/' component={() => {
92
-            switch (props.user.logged) {
93
-              case true:
94
-                return <Redirect to={{pathname: PAGE.WORKSPACE.ROOT, state: {from: props.location}}} />
95
-              case false:
96
-                return <Redirect to={{pathname: '/login', state: {from: props.location}}} />
97
-              case null:
98
-                return null
99
-            }
100
-          }} />
101
-
102
-          <Route path='/workspaces/:idws?' render={() => // Workspace Router
103
-            <div className='sidebarpagecontainer'>
104
-              <Sidebar />
105
-
106
-              <Route exact path={PAGE.WORKSPACE.ROOT} render={() => props.workspaceList.length === 0 // handle '/' and redirect to first workspace
107
-                ? null // @FIXME this needs to be handled in case of new user that has no workspace
108
-                : <Redirect to={{pathname: PAGE.WORKSPACE.DASHBOARD(props.workspaceList[0].id), state: {from: props.location}}} />
109
-              } />
110
-
111
-              <Route exact path={`${PAGE.WORKSPACE.ROOT}/:idws`} render={props2 => // handle '/workspaces/:id' and add '/contents'
112
-                <Redirect to={{pathname: PAGE.WORKSPACE.CONTENT_LIST(props2.match.params.idws), state: {from: props.location}}} />
113
-              } />
114
-
115
-              <Route path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />
116
-              <Route path={PAGE.WORKSPACE.CALENDAR(':idws')} component={() => <div><br /><br /><br /><br />NYI</div>} />
117
-              <Route path={PAGE.WORKSPACE.CONTENT(':idws', ':type', ':idcts')} component={WorkspaceContent} />
118
-              <Route exact path={PAGE.WORKSPACE.CONTENT_LIST(':idws')} component={WorkspaceContent} />
119
-            </div>
120
-          } />
121
-
122
-          <Route path={PAGE.ACCOUNT} render={() =>
123
-            <div className='sidebarpagecontainer'>
124
-              <Sidebar />
124
+        <div className='sidebarpagecontainer'>
125
+          <Sidebar />
126
+
127
+          <div className='tracim__content'>
128
+            <Route path={PAGE.LOGIN} component={Login} />
129
+
130
+            <Route exact path='/' component={() => {
131
+              switch (props.user.logged) {
132
+                case true:
133
+                  return <Redirect to={{pathname: PAGE.WORKSPACE.ROOT, state: {from: props.location}}} />
134
+                case false:
135
+                  return <Redirect to={{pathname: '/login', state: {from: props.location}}} />
136
+                case null:
137
+                  return null
138
+              }
139
+            }} />
140
+
141
+            <Route path='/workspaces/:idws?' render={() => // Workspace Router
142
+              <div>
143
+                <Route exact path={PAGE.WORKSPACE.ROOT} render={() => props.workspaceList.length === 0 // handle '/' and redirect to first workspace
144
+                  ? null // @FIXME this needs to be handled in case of new user that has no workspace
145
+                  : <Redirect to={{pathname: PAGE.WORKSPACE.DASHBOARD(props.workspaceList[0].id), state: {from: props.location}}} />
146
+                } />
147
+
148
+                <Route exact path={`${PAGE.WORKSPACE.ROOT}/:idws`} render={props2 => // handle '/workspaces/:id' and add '/contents'
149
+                  <Redirect to={{pathname: PAGE.WORKSPACE.CONTENT_LIST(props2.match.params.idws), state: {from: props.location}}} />
150
+                } />
151
+
152
+                <Route path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />
153
+                <Route path={PAGE.WORKSPACE.CALENDAR(':idws')} component={() => <div><br /><br /><br /><br />NYI</div>} />
154
+                <Route path={PAGE.WORKSPACE.CONTENT(':idws', ':type', ':idcts')} component={WorkspaceContent} />
155
+                <Route exact path={PAGE.WORKSPACE.CONTENT_LIST(':idws')} component={WorkspaceContent} />
156
+              </div>
157
+            } />
158
+
159
+            <Route path={PAGE.ACCOUNT} render={() =>
125 160
               <Account />
126
-            </div>
127
-          } />
128
-
129
-          <Route path={PAGE.ADMIN.ROOT} render={() =>
130
-            <div className='sidebarpagecontainer'>
131
-              <Sidebar />
161
+            } />
132 162
 
163
+            <Route path={PAGE.ADMIN.ROOT} render={() =>
133 164
               <AppFullscreenRouter />
134
-            </div>
135
-          } />
165
+            } />
136 166
 
137
-          <Route path={'/wip/:cp'} component={WIPcomponent} /> {/* for testing purpose only */}
167
+            <Route path={'/wip/:cp'} component={WIPcomponent} /> {/* for testing purpose only */}
138 168
 
139
-          <div id='appFeatureContainer' />
140
-          <div id='popupCreateContentContainer' />
169
+            <div id='appFeatureContainer' />
170
+            <div id='popupCreateContentContainer' />
171
+          </div>
141 172
         </div>
142
-
143 173
       </div>
144 174
     )
145 175
   }

+ 1 - 0
frontend/src/css/Generic.styl Просмотреть файл

@@ -8,6 +8,7 @@ a
8 8
   height 100%
9 9
   &__content
10 10
     height 100%
11
+    width 100%
11 12
 
12 13
 .btn-primary
13 14
   background-color thirdColor