Browse Source

[https://github.com/tracim/tracim/issues/745] added handler on app's btns + handler for btn 'see more' + fixed member's avatar

Skylsmoi 6 years ago
parent
commit
817be560ee

+ 2 - 2
frontend/src/action-creator.async.js View File

@@ -240,9 +240,9 @@ export const getWorkspaceContentList = (user, idWorkspace, idParent) => dispatch
240 240
   })
241 241
 }
242 242
 
243
-export const getWorkspaceRecentActivityList = (user, idWorkspace) => dispatch => {
243
+export const getWorkspaceRecentActivityList = (user, idWorkspace, beforeId = null) => dispatch => {
244 244
   return fetchWrapper({
245
-    url: `${FETCH_CONFIG.apiUrl}/users/${user.user_id}/workspaces/${idWorkspace}/contents/recently_active?limit=10`,
245
+    url: `${FETCH_CONFIG.apiUrl}/users/${user.user_id}/workspaces/${idWorkspace}/contents/recently_active?limit=10${beforeId ? `&before_content_id=${beforeId}` : ''}`,
246 246
     param: {
247 247
       headers: {
248 248
         ...FETCH_CONFIG.headers,

+ 2 - 2
frontend/src/action-creator.sync.js View File

@@ -2,6 +2,7 @@ export const SET = 'Set'
2 2
 export const UPDATE = 'Update'
3 3
 export const ADD = 'Add'
4 4
 export const REMOVE = 'Remove'
5
+export const APPEND = 'Append'
5 6
 
6 7
 export const TIMEZONE = 'Timezone'
7 8
 export const setTimezone = timezone => ({ type: `${SET}/${TIMEZONE}`, timezone })
@@ -52,8 +53,7 @@ export const WORKSPACE_MEMBER_ADD = `${WORKSPACE_MEMBER}/${ADD}`
52 53
 export const WORKSPACE_RECENT_ACTIVITY = `${WORKSPACE}/RecentActivity/List`
53 54
 export const WORKSPACE_RECENT_ACTIVITY_LIST = `${WORKSPACE_RECENT_ACTIVITY}/List`
54 55
 export const setWorkspaceRecentActivityList = workspaceRecentActivityList => ({ type: `${SET}/${WORKSPACE_RECENT_ACTIVITY_LIST}`, workspaceRecentActivityList })
55
-export const WORKSPACE_RECENT_ACTIVITY_FOR_USER_LIST = `${WORKSPACE_RECENT_ACTIVITY}/ForUser/List`
56
-export const setWorkspaceRecentActivityForUserList = workspaceRecentActivityForUserList => ({ type: `${SET}/${WORKSPACE_RECENT_ACTIVITY_FOR_USER_LIST}`, workspaceRecentActivityForUserList })
56
+export const appendWorkspaceRecentActivityList = workspaceRecentActivityList => ({ type: `${APPEND}/${WORKSPACE_RECENT_ACTIVITY_LIST}`, workspaceRecentActivityList })
57 57
 
58 58
 export const WORKSPACE_READ_STATUS = `${WORKSPACE}/ReadStatus`
59 59
 export const WORKSPACE_READ_STATUS_LIST = `${WORKSPACE_READ_STATUS}/List`

+ 3 - 1
frontend/src/component/Dashboard/ContentTypeBtn.jsx View File

@@ -15,6 +15,7 @@ export const ContentTypeBtn = props =>
15 15
         backgroundColor: color(props.hexcolor).darken(0.15).hexString()
16 16
       }
17 17
     }}
18
+    onClick={props.onClickBtn}
18 19
   >
19 20
     <div className={classnames(`${props.customClass}__text`)}>
20 21
       <div className={classnames(`${props.customClass}__text__icon`)}>
@@ -33,7 +34,8 @@ ContentTypeBtn.propTypes = {
33 34
   label: PropTypes.string.isRequired,
34 35
   faIcon: PropTypes.string.isRequired,
35 36
   creationLabel: PropTypes.string.isRequired,
36
-  customClass: PropTypes.string
37
+  customClass: PropTypes.string,
38
+  onClickBtn: PropTypes.func
37 39
 }
38 40
 
39 41
 ContentTypeBtn.defaultProps = {

+ 1 - 1
frontend/src/component/Dashboard/MemberList.jsx View File

@@ -45,7 +45,7 @@ export class MemberList extends React.Component {
45 45
                   {props.memberList.map(m =>
46 46
                     <li className='memberlist__list__item' key={m.id}>
47 47
                       <div className='memberlist__list__item__avatar'>
48
-                        {m.avatarUrl ? <img src={m.avatarUrl} /> : <img src='NYI' />}
48
+                        <img src={m.avatarUrl} />
49 49
                       </div>
50 50
 
51 51
                       <div className='memberlist__list__item__info mr-auto'>

+ 0 - 8
frontend/src/component/Dashboard/MemberList.styl View File

@@ -21,14 +21,6 @@
21 21
       display flex
22 22
       border-bottom 1px solid grey
23 23
       padding 10px 15px
24
-      &:hover
25
-        background-color fourthColor
26
-      &:nth-last-child(1)
27
-        border-bottom 0
28
-      &:nth-child(even)
29
-        background-color grey-hover
30
-        &:hover
31
-          background-color fourthColor
32 24
       &__avatar
33 25
         margin-right 20px
34 26
         & > img

+ 9 - 4
frontend/src/component/Dashboard/RecentActivity.jsx View File

@@ -20,11 +20,11 @@ export const RecentActivity = props =>
20 20
     </div>
21 21
 
22 22
     <div className='activity__wrapper'>
23
-      {props.recentActivityFilteredForUser.map(content => {
23
+      {props.recentActivityList.map(content => {
24 24
         const contentType = props.contentTypeList.find(ct => ct.slug === content.type)
25 25
         return (
26 26
           <div
27
-            className='activity__workspace'
27
+            className={classnames('activity__workspace', {'read': props.readByUserList.includes(content.id)})}
28 28
             onClick={() => props.onClickRecentContent(content.id, content.type)}
29 29
             key={content.id}
30 30
           >
@@ -53,7 +53,12 @@ export default RecentActivity
53 53
 
54 54
 RecentActivity.propTypes = {
55 55
   t: PropTypes.func.isRequired,
56
-  recentActivityFilteredForUser: PropTypes.array.isRequired,
56
+  recentActivityList: PropTypes.array.isRequired,
57 57
   contentTypeList: PropTypes.array.isRequired,
58
-  onClickSeeMore: PropTypes.func.isRequired
58
+  onClickSeeMore: PropTypes.func.isRequired,
59
+  readByUserList: PropTypes.array
60
+}
61
+
62
+RecentActivity.defaultProps = {
63
+  readByUserList: []
59 64
 }

+ 3 - 3
frontend/src/component/Dashboard/RecentActivity.styl View File

@@ -23,6 +23,9 @@
23 23
     border-bottom 1px solid grey
24 24
     padding 15px
25 25
     cursor pointer
26
+    font-weight bold
27
+    &.read
28
+      font-weight normal
26 29
     &:hover
27 30
       background-color fourthColor
28 31
     &:nth-child(even)
@@ -34,9 +37,6 @@
34 37
       font-size 25px
35 38
     &__name
36 39
       font-size 18px
37
-      font-weight 500
38
-      span
39
-        font-weight 400
40 40
   &__more
41 41
     &__btn
42 42
       margin 15px

+ 34 - 11
frontend/src/container/Dashboard.jsx View File

@@ -20,7 +20,7 @@ import {
20 20
   setWorkspaceDetail,
21 21
   setWorkspaceMemberList,
22 22
   setWorkspaceRecentActivityList,
23
-  setWorkspaceRecentActivityForUserList,
23
+  appendWorkspaceRecentActivityList,
24 24
   setWorkspaceReadStatusList
25 25
 } from '../action-creator.sync.js'
26 26
 import { ROLE, PAGE } from '../helper.js'
@@ -34,7 +34,7 @@ class Dashboard extends React.Component {
34 34
   constructor (props) {
35 35
     super(props)
36 36
     this.state = {
37
-      workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null, // this is used to avoid handling the parseInt everytime
37
+      workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null, // this is used to avoid handling the parseInt every time
38 38
       newMember: {
39 39
         id: '',
40 40
         avatarUrl: '',
@@ -51,6 +51,26 @@ class Dashboard extends React.Component {
51 51
   }
52 52
 
53 53
   async componentDidMount () {
54
+    this.loadWorkspaceDetail()
55
+    this.loadMemberList()
56
+    this.loadRecentActivity()
57
+  }
58
+
59
+  componentDidUpdate (prevProps, prevState) {
60
+    const { props, state } = this
61
+
62
+    if (prevProps.match.params.idws !== props.match.params.idws) {
63
+      this.setState({workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null})
64
+    }
65
+
66
+    if (prevState.workspaceIdInUrl !== state.workspaceIdInUrl) {
67
+      this.loadWorkspaceDetail()
68
+      this.loadMemberList()
69
+      this.loadRecentActivity()
70
+    }
71
+  }
72
+
73
+  loadWorkspaceDetail = async () => {
54 74
     const { props, state } = this
55 75
 
56 76
     const fetchWorkspaceDetail = await props.dispatch(getWorkspaceDetail(props.user, state.workspaceIdInUrl))
@@ -58,8 +78,6 @@ class Dashboard extends React.Component {
58 78
       case 200: props.dispatch(setWorkspaceDetail(fetchWorkspaceDetail.json)); break
59 79
       default: props.dispatch(newFlashMessage(`${props.t('An error has happened while fetching')} ${props.t('workspace detail')}`, 'warning')); break
60 80
     }
61
-    this.loadMemberList()
62
-    this.loadRecentActivity()
63 81
   }
64 82
 
65 83
   loadMemberList = async () => {
@@ -87,11 +105,6 @@ class Dashboard extends React.Component {
87 105
       case 200: props.dispatch(setWorkspaceReadStatusList(fetchWorkspaceReadStatusList.json)); break
88 106
       default: props.dispatch(newFlashMessage(`${props.t('An error has happened while fetching')} ${props.t('read status list')}`, 'warning')); break
89 107
     }
90
-
91
-    const readStatusForUserList = fetchWorkspaceReadStatusList.json.filter(c => c.read_by_user).map(c => c.content_id)
92
-    const recentActivityForUserList = fetchWorkspaceRecentActivityList.json.filter(content => !readStatusForUserList.includes(content.content_id))
93
-
94
-    props.dispatch(setWorkspaceRecentActivityForUserList(recentActivityForUserList))
95 108
   }
96 109
 
97 110
   handleToggleNewMemberDashboard = () => this.setState(prevState => ({displayNewMemberDashboard: !prevState.displayNewMemberDashboard}))
@@ -114,7 +127,15 @@ class Dashboard extends React.Component {
114 127
   }
115 128
 
116 129
   handleClickSeeMore = async () => {
117
-    console.log('nyi')
130
+    const { props, state } = this
131
+
132
+    const idLastRecentActivity = props.curWs.recentActivityList[props.curWs.recentActivityList.length - 1].id
133
+
134
+    const fetchWorkspaceRecentActivityList = await props.dispatch(getWorkspaceRecentActivityList(props.user, state.workspaceIdInUrl, idLastRecentActivity))
135
+    switch (fetchWorkspaceRecentActivityList.status) {
136
+      case 200: props.dispatch(appendWorkspaceRecentActivityList(fetchWorkspaceRecentActivityList.json)); break
137
+      default: props.dispatch(newFlashMessage(`${props.t('An error has happened while fetching')} ${props.t('recent activity list')}`, 'warning')); break
138
+    }
118 139
   }
119 140
 
120 141
   handleSearchUser = async userNameToSearch => {
@@ -223,6 +244,7 @@ class Dashboard extends React.Component {
223 244
                   label={ct.label}
224 245
                   faIcon={ct.faIcon}
225 246
                   creationLabel={ct.creationLabel}
247
+                  onClickBtn={() => props.history.push(PAGE.WORKSPACE.NEW(props.curWs.id, ct.slug))}
226 248
                   key={ct.label}
227 249
                 />
228 250
               )}
@@ -231,7 +253,8 @@ class Dashboard extends React.Component {
231 253
             <div className='dashboard__workspaceInfo'>
232 254
               <RecentActivity
233 255
                 customClass='dashboard__activity'
234
-                recentActivityFilteredForUser={props.curWs.recentActivityForUserList}
256
+                recentActivityList={props.curWs.recentActivityList}
257
+                readByUserList={props.curWs.contentReadStatusList}
235 258
                 contentTypeList={props.contentType}
236 259
                 onClickRecentContent={this.handleClickRecentContent}
237 260
                 onClickEverythingAsRead={this.handleClickMarkRecentActivityAsRead}

+ 2 - 2
frontend/src/container/Tracim.jsx View File

@@ -106,11 +106,11 @@ class Tracim extends React.Component {
106 106
 
107 107
               <Route exact path={PAGE.WORKSPACE.ROOT} render={() => props.workspaceList.length === 0 // handle '/' and redirect to first workspace
108 108
                 ? null // @FIXME this needs to be handled in case of new user that has no workspace
109
-                : <Redirect to={{pathname: `/workspaces/${props.workspaceList[0].id}/contents`, state: {from: props.location}}} />
109
+                : <Redirect to={{pathname: PAGE.WORKSPACE.DASHBOARD(props.workspaceList[0].id), state: {from: props.location}}} />
110 110
               } />
111 111
 
112 112
               <Route exact path={`${PAGE.WORKSPACE.ROOT}/:idws`} render={props2 => // handle '/workspaces/:id' and add '/contents'
113
-                <Redirect to={{pathname: `/workspaces/${props2.match.params.idws}/contents`, state: {from: props.location}}} />
113
+                <Redirect to={{pathname: PAGE.WORKSPACE.CONTENT_LIST(props2.match.params.idws), state: {from: props.location}}} />
114 114
               } />
115 115
 
116 116
               <Route path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />

+ 22 - 15
frontend/src/reducer/currentWorkspace.js View File

@@ -1,11 +1,13 @@
1 1
 import {
2 2
   SET,
3
+  APPEND,
3 4
   WORKSPACE_DETAIL,
4 5
   WORKSPACE_MEMBER_LIST,
5
-  WORKSPACE_READ_STATUS_LIST, WORKSPACE_RECENT_ACTIVITY_FOR_USER_LIST,
6
+  WORKSPACE_READ_STATUS_LIST,
6 7
   WORKSPACE_RECENT_ACTIVITY_LIST
7 8
 } from '../action-creator.sync.js'
8 9
 import { handleRouteFromApi } from '../helper.js'
10
+import { generateAvatarFromPublicName } from 'tracim_frontend_lib'
9 11
 
10 12
 const defaultWorkspace = {
11 13
   id: 0,
@@ -43,7 +45,9 @@ export default function currentWorkspace (state = defaultWorkspace, action) {
43 45
         memberList: action.workspaceMemberList.map(m => ({
44 46
           id: m.user_id,
45 47
           publicName: m.user.public_name,
46
-          avatarUrl: m.user.avatar_url,
48
+          avatarUrl: m.user.avatar_url
49
+            ? m.user.avatar_url
50
+            : m.user.public_name ? generateAvatarFromPublicName(m.user.public_name) : '',
47 51
           role: m.role,
48 52
           isActive: m.is_active
49 53
         }))
@@ -66,21 +70,24 @@ export default function currentWorkspace (state = defaultWorkspace, action) {
66 70
         }))
67 71
       }
68 72
 
69
-    case `${SET}/${WORKSPACE_RECENT_ACTIVITY_FOR_USER_LIST}`:
73
+    case `${APPEND}/${WORKSPACE_RECENT_ACTIVITY_LIST}`:
70 74
       return {
71 75
         ...state,
72
-        recentActivityForUserList: action.workspaceRecentActivityForUserList.map(ra => ({
73
-          id: ra.content_id,
74
-          slug: ra.slug,
75
-          label: ra.label,
76
-          type: ra.content_type,
77
-          idParent: ra.parent_id,
78
-          showInUi: ra.show_in_ui,
79
-          isArchived: ra.is_archived,
80
-          isDeleted: ra.is_deleted,
81
-          statusSlug: ra.status,
82
-          subContentTypeSlug: ra.sub_content_types
83
-        }))
76
+        recentActivityList: [
77
+          ...state.recentActivityList,
78
+          ...action.workspaceRecentActivityList.map(ra => ({
79
+            id: ra.content_id,
80
+            slug: ra.slug,
81
+            label: ra.label,
82
+            type: ra.content_type,
83
+            idParent: ra.parent_id,
84
+            showInUi: ra.show_in_ui,
85
+            isArchived: ra.is_archived,
86
+            isDeleted: ra.is_deleted,
87
+            statusSlug: ra.status,
88
+            subContentTypeSlug: ra.sub_content_types
89
+          }))
90
+        ]
84 91
       }
85 92
 
86 93
     case `${SET}/${WORKSPACE_READ_STATUS_LIST}`: