Browse Source

app now handles multi users

Skylsmoi 5 years ago
parent
commit
9e20d8e5f7

+ 6 - 2
dist/index.html View File

@@ -42,6 +42,8 @@
42 42
             return appThread
43 43
           case 'file':
44 44
             return appFile
45
+          default:
46
+            return null
45 47
         }
46 48
       }
47 49
 
@@ -74,13 +76,15 @@
74 76
       }
75 77
 
76 78
       GLOBAL_eventReducer = ({ detail: { type, data } }) => {
77
-        console.log('GLOBAL_eventReducer', type, data)
78
-
79 79
         switch (type) {
80 80
           case 'hide_popupCreateContent':
81
+            console.log('GLOBAL_eventReducer', type, data)
81 82
             getSelectedApp(data.name).unmountApp('popupCreateContentContainer')
82 83
             break
83 84
           case 'unmount_app':
85
+            console.log('GLOBAL_eventReducer', type, data)
86
+            if (prevSelectedApp.name === '') return
87
+
84 88
             prevSelectedApp.unmountApp('appContainer')
85 89
             prevSelectedApp.unmountApp('popupCreateContentContainer')
86 90
             prevSelectedApp.isRendered = false

+ 1 - 0
package.json View File

@@ -28,6 +28,7 @@
28 28
     "css-loader": "^0.28.7",
29 29
     "file-loader": "^1.1.5",
30 30
     "i18next": "^10.5.0",
31
+    "js-cookie": "^2.2.0",
31 32
     "prop-types": "^15.6.0",
32 33
     "query-string": "^6.1.0",
33 34
     "react": "^16.0.0",

+ 31 - 14
src/action-creator.async.js View File

@@ -97,8 +97,8 @@ export const postUserLogin = (login, password, rememberMe) => async dispatch =>
97 97
       method: 'POST',
98 98
       body: JSON.stringify({
99 99
         email: login,
100
-        password: password,
101
-        remember_me: rememberMe
100
+        password: password
101
+        // remember_me: rememberMe
102 102
       })
103 103
     },
104 104
     actionName: USER_LOGIN,
@@ -118,11 +118,14 @@ export const postUserLogout = () => async dispatch => {
118 118
   })
119 119
 }
120 120
 
121
-export const getUserIsConnected = () => async dispatch => {
121
+export const getUserIsConnected = user => async dispatch => {
122 122
   return fetchWrapper({
123 123
     url: `${FETCH_CONFIG.apiUrl}/sessions/whoami`, // FETCH_CONFIG.apiUrl
124 124
     param: {
125
-      headers: {...FETCH_CONFIG.headers},
125
+      headers: {
126
+        ...FETCH_CONFIG.headers,
127
+        'Authorization': 'Basic ' + user.auth
128
+      },
126 129
       method: 'GET'
127 130
     },
128 131
     actionName: USER_CONNECTED,
@@ -143,11 +146,14 @@ export const getUserRole = user => async dispatch => {
143 146
   if (fetchGetUserRole.status === 200) dispatch(setUserRole(fetchGetUserRole.json))
144 147
 }
145 148
 
146
-export const getWorkspaceList = idUser => dispatch => {
149
+export const getWorkspaceList = user => dispatch => {
147 150
   return fetchWrapper({
148
-    url: `${FETCH_CONFIG.apiUrl}/users/${idUser}/workspaces`,
151
+    url: `${FETCH_CONFIG.apiUrl}/users/${user.user_id}/workspaces`,
149 152
     param: {
150
-      headers: {...FETCH_CONFIG.headers},
153
+      headers: {
154
+        ...FETCH_CONFIG.headers,
155
+        'Authorization': 'Basic ' + user.auth
156
+      },
151 157
       method: 'GET'
152 158
     },
153 159
     actionName: WORKSPACE_LIST,
@@ -155,11 +161,14 @@ export const getWorkspaceList = idUser => dispatch => {
155 161
   })
156 162
 }
157 163
 
158
-export const getWorkspaceContentList = (idWorkspace, idParent) => dispatch => {
164
+export const getWorkspaceContentList = (user, idWorkspace, idParent) => dispatch => {
159 165
   return fetchWrapper({
160 166
     url: `${FETCH_CONFIG.apiUrl}/workspaces/${idWorkspace}/contents?parent_id=${idParent}`,
161 167
     param: {
162
-      headers: {...FETCH_CONFIG.headers},
168
+      headers: {
169
+        ...FETCH_CONFIG.headers,
170
+        'Authorization': 'Basic ' + user.auth
171
+      },
163 172
       method: 'GET'
164 173
     },
165 174
     actionName: WORKSPACE,
@@ -192,23 +201,31 @@ export const getFolderContent = (idWorkspace, idFolder) => async dispatch => {
192 201
   if (fetchGetFolderContent.status === 200) dispatch(setFolderData(idFolder, fetchGetFolderContent.json))
193 202
 }
194 203
 
195
-export const getAppList = () => dispatch => {
204
+export const getAppList = user => dispatch => {
205
+  console.log(user)
196 206
   return fetchWrapper({
197 207
     url: `${FETCH_CONFIG.apiUrl}/system/applications`,
198 208
     param: {
199
-      headers: {...FETCH_CONFIG.headers},
200
-      method: 'GET'
209
+      headers: {
210
+        ...FETCH_CONFIG.headers,
211
+        'Authorization': 'Basic ' + user.auth
212
+      },
213
+      method: 'GET',
214
+      'Authorization': 'Basic ' + user.auth
201 215
     },
202 216
     actionName: APP_LIST,
203 217
     dispatch
204 218
   })
205 219
 }
206 220
 
207
-export const getContentTypeList = () => dispatch => {
221
+export const getContentTypeList = user => dispatch => {
208 222
   return fetchWrapper({
209 223
     url: `${FETCH_CONFIG.apiUrl}/system/content_types`,
210 224
     param: {
211
-      headers: {...FETCH_CONFIG.headers},
225
+      headers: {
226
+        ...FETCH_CONFIG.headers,
227
+        'Authorization': 'Basic ' + user.auth
228
+      },
212 229
       method: 'GET'
213 230
     },
214 231
     actionName: CONTENT_TYPE_LIST,

+ 3 - 3
src/container/Dashboard.jsx View File

@@ -30,17 +30,17 @@ class Dashboard extends React.Component {
30 30
     console.log('<Dashboard> componentDidMount')
31 31
 
32 32
     if (app.length === 0) {
33
-      const fetchGetAppList = await dispatch(getAppList())
33
+      const fetchGetAppList = await dispatch(getAppList(user))
34 34
       if (fetchGetAppList.status === 200) dispatch(setAppList(fetchGetAppList.json))
35 35
     }
36 36
 
37 37
     if (contentType.length === 0) {
38
-      const fetchGetContentTypeList = await dispatch(getContentTypeList())
38
+      const fetchGetContentTypeList = await dispatch(getContentTypeList(user))
39 39
       if (fetchGetContentTypeList.status === 200) dispatch(setContentTypeList(fetchGetContentTypeList.json))
40 40
     }
41 41
 
42 42
     if (user.user_id !== -1 && workspaceList.length === 0) {
43
-      const fetchGetWorkspaceList = await dispatch(getWorkspaceList(user.user_id))
43
+      const fetchGetWorkspaceList = await dispatch(getWorkspaceList(user))
44 44
 
45 45
       if (fetchGetWorkspaceList.status === 200) {
46 46
         dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))

+ 9 - 2
src/container/Header.jsx View File

@@ -20,6 +20,7 @@ import {
20 20
 import {
21 21
   postUserLogout
22 22
 } from '../action-creator.async.js'
23
+import Cookies from 'js-cookie'
23 24
 
24 25
 class Header extends React.Component {
25 26
   handleClickLogo = () => {}
@@ -42,8 +43,14 @@ class Header extends React.Component {
42 43
     const { dispatch, t } = this.props
43 44
 
44 45
     const fetchPostUserLogout = await dispatch(postUserLogout())
45
-    if (fetchPostUserLogout.status === 204) dispatch(setUserDisconnected())
46
-    else dispatch(newFlashMessage(t('Login.logout_error', 'danger')))
46
+    if (fetchPostUserLogout.status === 204) {
47
+      Cookies.remove('user_login')
48
+      Cookies.remove('user_auth')
49
+
50
+      dispatch(setUserDisconnected())
51
+    } else {
52
+      dispatch(newFlashMessage(t('Login.logout_error', 'danger')))
53
+    }
47 54
   }
48 55
 
49 56
   render () {

+ 15 - 3
src/container/Login.jsx View File

@@ -9,14 +9,15 @@ import Card from '../component/common/Card/Card.jsx'
9 9
 import CardHeader from '../component/common/Card/CardHeader.jsx'
10 10
 import CardBody from '../component/common/Card/CardBody.jsx'
11 11
 import InputGroupText from '../component/common/Input/InputGroupText.jsx'
12
-import InputCheckbox from '../component/common/Input/InputCheckbox.jsx'
12
+// import InputCheckbox from '../component/common/Input/InputCheckbox.jsx'
13 13
 import Button from '../component/common/Input/Button.jsx'
14 14
 import LoginBtnForgotPw from '../component/Login/LoginBtnForgotPw.jsx'
15 15
 import {
16 16
   newFlashMessage,
17 17
   setUserConnected
18 18
 } from '../action-creator.sync.js'
19
-import {PAGE} from '../helper.js'
19
+import { PAGE } from '../helper.js'
20
+import Cookies from 'js-cookie'
20 21
 
21 22
 class Login extends React.Component {
22 23
   constructor (props) {
@@ -43,9 +44,18 @@ class Login extends React.Component {
43 44
     const { inputLogin, inputPassword, inputRememberMe } = this.state
44 45
 
45 46
     const fetchPostUserLogin = await dispatch(postUserLogin(inputLogin.value, inputPassword.value, inputRememberMe))
47
+    const userAuth = btoa(`${inputLogin.value}:${inputPassword.value}`)
46 48
 
47 49
     if (fetchPostUserLogin.status === 200) {
48
-      dispatch(setUserConnected({...fetchPostUserLogin.json, logged: true}))
50
+      dispatch(setUserConnected({
51
+        ...fetchPostUserLogin.json,
52
+        auth: userAuth,
53
+        logged: true
54
+      }))
55
+
56
+      Cookies.set('user_login', inputLogin.value)
57
+      Cookies.set('user_auth', userAuth)
58
+
49 59
       history.push(PAGE.HOME)
50 60
     } else if (fetchPostUserLogin.status === 400) {
51 61
       dispatch(newFlashMessage(t('Login.fail'), 'danger'))
@@ -95,6 +105,7 @@ class Login extends React.Component {
95 105
 
96 106
                       <div className='row mt-4 mb-4'>
97 107
                         <div className='col-12 col-sm-6 col-md-6 col-lg-6 col-xl-6'>
108
+                          {/*
98 109
                           <InputCheckbox
99 110
                             parentClassName='connection__form__rememberme'
100 111
                             customClass=''
@@ -102,6 +113,7 @@ class Login extends React.Component {
102 113
                             checked={this.state.inputRememberMe}
103 114
                             onChange={this.handleChangeRememberMe}
104 115
                           />
116
+                          */}
105 117
                         </div>
106 118
 
107 119
                         <div className='col-12 col-sm-6 col-md-6 col-lg-6 col-xl-6 text-sm-right'>

+ 1 - 1
src/container/OpenContentApp.jsx View File

@@ -27,7 +27,7 @@ export class OpenContentApp extends React.Component {
27 27
         })
28 28
       } else { // open another app
29 29
         // if another app is already visible, hide it
30
-        if (appOpenedType !== false) GLOBAL_dispatchEvent(`${appOpenedType}_hideApp`)
30
+        if (appOpenedType !== false) GLOBAL_dispatchEvent({type: `${appOpenedType}_hideApp`})
31 31
         // open app
32 32
         renderApp(
33 33
           contentType.find(ct => ct.slug === contentToOpen.type),

+ 1 - 1
src/container/Sidebar.jsx View File

@@ -23,7 +23,7 @@ class Sidebar extends React.Component {
23 23
 
24 24
   componentDidUpdate (prevProps, prevState) {
25 25
     console.log('<Sidebar> Did Update')
26
-    if (this.props.match.params.idws === undefined) return
26
+    if (this.props.match.params.idws === undefined || isNaN(this.props.match.params.idws)) return
27 27
 
28 28
     const newWorkspaceId = parseInt(this.props.match.params.idws)
29 29
     if (prevState.workspaceIdInUrl !== newWorkspaceId) this.setState({workspaceIdInUrl: newWorkspaceId})

+ 30 - 23
src/container/Tracim.jsx View File

@@ -10,9 +10,7 @@ import FlashMessage from '../component/FlashMessage.jsx'
10 10
 import WorkspaceContent from './WorkspaceContent.jsx'
11 11
 import WIPcomponent from './WIPcomponent.jsx'
12 12
 import {
13
-  Route,
14
-  withRouter,
15
-  Switch
13
+  Route, withRouter, Switch
16 14
 } from 'react-router-dom'
17 15
 import PrivateRoute from './PrivateRoute.jsx'
18 16
 import { PAGE } from '../helper.js'
@@ -23,15 +21,28 @@ import {
23 21
   removeFlashMessage,
24 22
   setUserConnected
25 23
 } from '../action-creator.sync.js'
24
+import Cookies from 'js-cookie'
26 25
 
27 26
 class Tracim extends React.Component {
28 27
   async componentDidMount () {
29 28
     const { dispatch } = this.props
30 29
 
31
-    const fetchGetUserIsConnected = await dispatch(getUserIsConnected())
30
+    const userFromCookies = {
31
+      email: Cookies.get('user_login'),
32
+      auth: Cookies.get('user_auth')
33
+    }
34
+
35
+    if (userFromCookies.email === undefined || userFromCookies.auth === undefined) return
36
+
37
+    const fetchGetUserIsConnected = await dispatch(getUserIsConnected(userFromCookies))
32 38
     switch (fetchGetUserIsConnected.status) {
33 39
       case 200:
34
-        dispatch(setUserConnected({...fetchGetUserIsConnected.json, logged: true})); break
40
+        dispatch(setUserConnected({
41
+          ...fetchGetUserIsConnected.json,
42
+          auth: userFromCookies.auth,
43
+          logged: true
44
+        }))
45
+        break
35 46
       case 401:
36 47
         dispatch(setUserConnected({logged: false})); break
37 48
       default:
@@ -42,34 +53,30 @@ class Tracim extends React.Component {
42 53
   handleRemoveFlashMessage = msg => this.props.dispatch(removeFlashMessage(msg))
43 54
 
44 55
   render () {
45
-    const { flashMessage, user, t } = this.props
56
+    const { flashMessage, t } = this.props
46 57
 
47 58
     return (
48 59
       <div className='tracim'>
49 60
         <Header />
50 61
         <FlashMessage flashMessage={flashMessage} removeFlashMessage={this.handleRemoveFlashMessage} t={t} />
51 62
 
52
-        { user.logged === undefined
53
-          ? (<div />) // while we dont know if user is connected, display nothing but the header @TODO show loader
54
-          : (
55
-            <div className='tracim__content'>
56
-              <Route path={PAGE.LOGIN} component={Login} />
63
+        <div className='tracim__content'>
64
+          <Route path={PAGE.LOGIN} component={Login} />
65
+
66
+          <PrivateRoute exact path='/' component={WorkspaceContent} />
57 67
 
58
-              <PrivateRoute exact path='/' component={WorkspaceContent} />
68
+          <Switch>
69
+            <PrivateRoute path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />
70
+            <PrivateRoute path={PAGE.WORKSPACE.CALENDAR(':idws')} component={() => <div><br /><br /><br /><br />NYI</div>} />
71
+            <PrivateRoute path={PAGE.WORKSPACE.CONTENT(':idws', ':type?', ':idcts?')} component={WorkspaceContent} />
72
+          </Switch>
59 73
 
60
-              <Switch>
61
-                <PrivateRoute path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />
62
-                <PrivateRoute path={PAGE.WORKSPACE.CALENDAR(':idws')} component={() => <div><br /><br /><br /><br />NYI</div>} />
63
-                <PrivateRoute path={PAGE.WORKSPACE.CONTENT(':idws', ':type?', ':idcts?')} component={WorkspaceContent} />
64
-              </Switch>
74
+          <PrivateRoute path={PAGE.ACCOUNT} component={Account} />
75
+          <PrivateRoute path={'/wip/:cp'} component={WIPcomponent} /> {/* for testing purpose only */}
65 76
 
66
-              <PrivateRoute path={PAGE.ACCOUNT} component={Account} />
67
-              <PrivateRoute path={'/wip/:cp'} component={WIPcomponent} /> {/* for testing purpose only */}
77
+          <Footer />
78
+        </div>
68 79
 
69
-              <Footer />
70
-            </div>
71
-          )
72
-        }
73 80
       </div>
74 81
     )
75 82
   }

+ 12 - 11
src/container/WorkspaceContent.jsx View File

@@ -47,14 +47,16 @@ class WorkspaceContent extends React.Component {
47 47
   }
48 48
 
49 49
   customEventReducer = ({ detail: { type, data } }) => {
50
-    console.log(type, data)
51 50
     switch (type) {
52 51
       case 'openContentUrl':
52
+        console.log('<WorkspaceContent>', type, data)
53 53
         this.props.history.push(PAGE.WORKSPACE.CONTENT(data.idWorkspace, data.contentType, data.idContent))
54 54
         break
55 55
       case 'appClosed':
56
+        console.log('<WorkspaceContent>', type, data, this.state.workspaceIdInUrl)
57
+        if (this.state.workspaceIdInUrl === null) return // @FIXME: find out why when app is closed, workspaceInUrl is null then has it's proper value
56 58
         this.props.history.push(PAGE.WORKSPACE.CONTENT_LIST(this.state.workspaceIdInUrl))
57
-        this.setState({appOpened: false})
59
+        this.setState({appOpenedType: false})
58 60
         break
59 61
     }
60 62
   }
@@ -66,12 +68,12 @@ class WorkspaceContent extends React.Component {
66 68
     console.log('<WorkspaceContent> componentDidMount')
67 69
 
68 70
     if (app.length === 0) {
69
-      const fetchGetAppList = await dispatch(getAppList())
71
+      const fetchGetAppList = await dispatch(getAppList(user))
70 72
       if (fetchGetAppList.status === 200) dispatch(setAppList(fetchGetAppList.json))
71 73
     }
72 74
 
73 75
     if (contentType.length === 0) {
74
-      const fetchGetContentTypeList = await dispatch(getContentTypeList())
76
+      const fetchGetContentTypeList = await dispatch(getContentTypeList(user))
75 77
       if (fetchGetContentTypeList.status === 200) dispatch(setContentTypeList(fetchGetContentTypeList.json))
76 78
     }
77 79
 
@@ -79,7 +81,7 @@ class WorkspaceContent extends React.Component {
79 81
     if (match.params.idws !== undefined) wsToLoad = match.params.idws
80 82
 
81 83
     if (user.user_id !== -1 && workspaceList.length === 0) {
82
-      const fetchGetWorkspaceList = await dispatch(getWorkspaceList(user.user_id))
84
+      const fetchGetWorkspaceList = await dispatch(getWorkspaceList(user))
83 85
 
84 86
       if (fetchGetWorkspaceList.status === 200) {
85 87
         dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))
@@ -93,14 +95,14 @@ class WorkspaceContent extends React.Component {
93 95
 
94 96
     if (wsToLoad === null) return // ws already loaded
95 97
 
96
-    const wsContent = await dispatch(getWorkspaceContentList(wsToLoad, 0))
98
+    const wsContent = await dispatch(getWorkspaceContentList(user, wsToLoad, 0))
97 99
 
98 100
     if (wsContent.status === 200) dispatch(setWorkspaceContent(wsContent.json, qs.parse(location.search).type))
99 101
     else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
100 102
   }
101 103
 
102 104
   async componentDidUpdate (prevProps, prevState) {
103
-    const { match, location, dispatch } = this.props
105
+    const { user, match, location, dispatch } = this.props
104 106
 
105 107
     console.log('<WorkspaceContent> componentDidUpdate')
106 108
 
@@ -113,7 +115,7 @@ class WorkspaceContent extends React.Component {
113 115
     if (prevState.workspaceIdInUrl !== idWorkspace) {
114 116
       this.setState({workspaceIdInUrl: idWorkspace})
115 117
 
116
-      const wsContent = await dispatch(getWorkspaceContentList(idWorkspace, 0))
118
+      const wsContent = await dispatch(getWorkspaceContentList(user, idWorkspace, 0))
117 119
 
118 120
       if (wsContent.status === 200) dispatch(setWorkspaceContent(wsContent.json, qs.parse(location.search).type))
119 121
       else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
@@ -173,7 +175,7 @@ class WorkspaceContent extends React.Component {
173 175
   handleUpdateAppOpenedType = openedAppType => this.setState({appOpenedType: openedAppType})
174 176
 
175 177
   render () {
176
-    const { workspaceContent, contentType, match } = this.props
178
+    const { workspaceContent, contentType } = this.props
177 179
 
178 180
     const filterWorkspaceContent = (contentList, filter) => {
179 181
       return filter.length === 0
@@ -189,14 +191,13 @@ class WorkspaceContent extends React.Component {
189 191
     const filteredWorkspaceContent = workspaceContent.length > 0
190 192
       ? filterWorkspaceContent(workspaceContent, urlFilter ? [urlFilter] : [])
191 193
       : []
192
-    console.log('workspaceContent => filteredWorkspaceContent', filteredWorkspaceContent, 'urlFilter', urlFilter)
193 194
 
194 195
     return (
195 196
       <div className='sidebarpagecontainer'>
196 197
         <Sidebar />
197 198
 
198 199
         <OpenContentApp
199
-          idWorkspace={match.params.idws}
200
+          idWorkspace={this.state.workspaceIdInUrl}
200 201
           appOpenedType={this.state.appOpenedType}
201 202
           updateAppOpenedType={this.handleUpdateAppOpenedType}
202 203
         />

+ 1 - 2
src/helper.js View File

@@ -1,8 +1,7 @@
1 1
 export const FETCH_CONFIG = {
2 2
   headers: {
3 3
     'Accept': 'application/json',
4
-    'Content-Type': 'application/json',
5
-    'Authorization': 'Basic ' + btoa(`${'admin@admin.admin'}:${'admin@admin.admin'}`)
4
+    'Content-Type': 'application/json'
6 5
   },
7 6
   apiUrl: 'http://localhost:6543/api/v2',
8 7
   mockApiUrl: 'http://localhost:3001'

+ 1 - 1
src/reducer/user.js View File

@@ -6,7 +6,7 @@ import {
6 6
 
7 7
 const defaultUser = {
8 8
   user_id: -1,
9
-  logged: undefined, // undefined avoid to be redirected to /login while whoami ep has not responded yet
9
+  logged: false, // undefined avoid to be redirected to /login while whoami ep has not responded yet
10 10
   timezone: '',
11 11
   profile: {
12 12
     id: 1,