Browse Source

[https://github.com/tracim/tracim/issues/644] added autogenerated avatar when missing

Skylsmoi 5 years ago
parent
commit
2af4b70d4d

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

@@ -6,6 +6,7 @@ import {
6 6
   USER_DATA,
7 7
   USER_LANG
8 8
 } from '../action-creator.sync.js'
9
+import { generateAvatarFromPublicName } from 'tracim_frontend_lib'
9 10
 
10 11
 const defaultUser = {
11 12
   user_id: -1,
@@ -31,7 +32,9 @@ export default function user (state = defaultUser, action) {
31 32
       return {
32 33
         ...state,
33 34
         ...action.user,
34
-        avatar_url: 'https://www.algoo.fr/static/images/people_images/PERSO_SEUL.png' // @FIXME use avatar from api when db handles it
35
+        avatar_url: action.user.avatar_url
36
+          ? action.user.avatar_url
37
+          : generateAvatarFromPublicName(action.user.public_name)
35 38
       }
36 39
 
37 40
     case `${SET}/${USER_DISCONNECTED}`:

+ 12 - 2
frontend_app_html-document/src/container/HtmlDocument.jsx View File

@@ -5,6 +5,7 @@ import i18n from '../i18n.js'
5 5
 import {
6 6
   addAllResourceI18n,
7 7
   handleFetchResult,
8
+  generateAvatarFromPublicName,
8 9
   PopinFixed,
9 10
   PopinFixedHeader,
10 11
   PopinFixedOption,
@@ -119,7 +120,16 @@ class HtmlDocument extends React.Component {
119 120
       handleFetchResult(await fetchResultRevision)
120 121
     ])
121 122
       .then(([resComment, resRevision]) => {
122
-        const resCommentWithProperDate = resComment.body.map(c => ({...c, created: (new Date(c.created)).toLocaleString()}))
123
+        const resCommentWithProperDateAndAvatar = resComment.body.map(c => ({
124
+          ...c,
125
+          created: (new Date(c.created)).toLocaleString(),
126
+          author: {
127
+            ...c.author,
128
+            avatar_url: c.author.avatar_url
129
+              ? c.author.avatar_url
130
+              : generateAvatarFromPublicName(c.author.public_name)
131
+          }
132
+        }))
123 133
 
124 134
         const revisionWithComment = resRevision.body
125 135
           .map((r, i) => ({
@@ -128,7 +138,7 @@ class HtmlDocument extends React.Component {
128 138
             timelineType: 'revision',
129 139
             commentList: r.comment_ids.map(ci => ({
130 140
               timelineType: 'comment',
131
-              ...resCommentWithProperDate.find(c => c.content_id === ci)
141
+              ...resCommentWithProperDateAndAvatar.find(c => c.content_id === ci)
132 142
             })),
133 143
             number: i + 1
134 144
           }))

+ 0 - 3
frontend_app_html-document/src/css/index.styl View File

@@ -37,9 +37,6 @@
37 37
     &__messagelist
38 38
       min-height 70px
39 39
       &__item
40
-        &__avatar
41
-          border 1px solid darkHtmlColor
42
-          background-color off-white
43 40
         &__content
44 41
           color darkGrey
45 42
       &__version

+ 8 - 1
frontend_app_thread/src/container/Thread.jsx View File

@@ -4,6 +4,7 @@ import { debug } from '../helper.js'
4 4
 import {
5 5
   addAllResourceI18n,
6 6
   handleFetchResult,
7
+  generateAvatarFromPublicName,
7 8
   PopinFixed,
8 9
   PopinFixedHeader,
9 10
   PopinFixedOption,
@@ -108,7 +109,13 @@ class Thread extends React.Component {
108 109
         listMessage: resComment.body.map(c => ({
109 110
           ...c,
110 111
           timelineType: 'comment',
111
-          created: (new Date(c.created)).toLocaleString()
112
+          created: (new Date(c.created)).toLocaleString(),
113
+          author: {
114
+            ...c.author,
115
+            avatar_url: c.author.avatar_url
116
+              ? c.author.avatar_url
117
+              : generateAvatarFromPublicName(c.author.public_name)
118
+          }
112 119
         }))
113 120
       }))
114 121
       .catch(e => console.log('Error loading Thread data.', e))

+ 0 - 4
frontend_app_thread/src/css/index.styl View File

@@ -16,10 +16,6 @@
16 16
     &__contentpage
17 17
       &__content
18 18
         width 100%
19
-      &__messagelist
20
-        &__item__avatar
21
-          border 1px solid darkenThread
22
-          background-color off-white
23 19
       &__texteditor
24 20
         flex 0 0 auto
25 21
 

+ 5 - 5
frontend_lib/dist/index.html View File

@@ -6,14 +6,14 @@
6 6
   <title>Tracim Lib</title>
7 7
   <link rel='shortcut icon' href='favicon.ico'>
8 8
 
9
-  <link rel="stylesheet" type="text/css" href="./font/font-awesome-4.7.0/css/font-awesome.css">
9
+  <link rel="stylesheet" type="text/css" href="./asset/font/font-awesome-4.7.0/css/font-awesome.css">
10 10
   <link href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700" rel="stylesheet">
11
-  <link rel="stylesheet" type="text/css" href="./dev/bootstrap-4.0.0-beta.css">
11
+  <link rel="stylesheet" type="text/css" href="./asset/bootstrap/bootstrap-4.0.0-beta.css">
12 12
 </head>
13 13
 <body>
14
-  <script src="./dev/jquery-3.2.1.js"></script>
15
-  <script src="./dev/popper-1.12.3.js"></script>
16
-  <script src="./dev/bootstrap-4.0.0-beta.2.js"></script>
14
+  <script src="./asset/bootstrap/jquery-3.2.1.js"></script>
15
+  <script src="./asset/bootstrap/popper-1.12.3.js"></script>
16
+  <script src="./asset/bootstrap/bootstrap-4.0.0-beta.2.js"></script>
17 17
 
18 18
   <div id='content'></div>
19 19
 

+ 1 - 1
frontend_lib/package.json View File

@@ -50,7 +50,7 @@
50 50
     "webpack-dev-server": "^2.9.2"
51 51
   },
52 52
   "standard": {
53
-    "globals": [],
53
+    "globals": ["G_vmlCanvasManager"],
54 54
     "parser": "babel-eslint",
55 55
     "ignore": []
56 56
   },

+ 1 - 1
frontend_lib/src/component/Timeline/Comment.jsx View File

@@ -22,7 +22,7 @@ const Comment = props => {
22 22
     )}>
23 23
       <div className={classnames(`${props.customClass}__messagelist__item__wrapper`, 'timeline__body__messagelist__item__wrapper')}>
24 24
         <div className={classnames(`${props.customClass}__messagelist__item__avatar`, 'timeline__body__messagelist__item__avatar')}>
25
-          {props.avatar ? <img src={props.avatar} /> : ''}
25
+          <img src={props.avatar} />
26 26
         </div>
27 27
       </div>
28 28
       <div className={classnames(`${props.customClass}__messagelist__item__authorandhour`, 'timeline__body__messagelist__item__authorandhour')}>

+ 33 - 0
frontend_lib/src/helper.js View File

@@ -1,3 +1,5 @@
1
+import color from 'color'
2
+
1 3
 export const libHandleFetchResult = async fetchResult => {
2 4
   switch (fetchResult.status) {
3 5
     case 200:
@@ -28,3 +30,34 @@ export const libAddAllResourceI18n = (i18n, translation) => {
28 30
     )
29 31
   )
30 32
 }
33
+
34
+export const libGenerateAvatarFromPublicName = publicName => {
35
+  // code from https://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-javascript
36
+  const stringToHashCode = str => str.split('').reduce((acc, char) => char.charCodeAt(0) + ((acc << 5) - acc), 0)
37
+
38
+  const intToRGB = i => {
39
+    const c = (i & 0x00FFFFFF).toString(16).toUpperCase()
40
+    return '00000'.substring(0, 6 - c.length) + c
41
+  }
42
+
43
+  const hexcolor = '#' + intToRGB(stringToHashCode(publicName))
44
+
45
+  let canvas = document.createElement('canvas')
46
+
47
+  // http://code.google.com/p/explorercanvas/wiki/Instructions#Dynamically_created_elements
48
+  if (!canvas.getContext) G_vmlCanvasManager.initElement(canvas)
49
+
50
+  let ctx = canvas.getContext('2d')
51
+  canvas.width = 44
52
+  canvas.height = 44
53
+
54
+  const { r, g, b } = color(hexcolor).desaturate(0.75).rgb()
55
+
56
+  ctx.beginPath()
57
+  ctx.arc(22, 22, 20, 0, 2 * Math.PI, false)
58
+  ctx.fillStyle = 'rgba(' + [r, g, b, 1].join() + ')'
59
+  ctx.fill()
60
+  ctx.stroke()
61
+
62
+  return canvas.toDataURL('image/png', '')
63
+}

+ 13 - 1
frontend_lib/src/index.dev.js View File

@@ -18,6 +18,7 @@ import Delimiter from './component/Delimiter/Delimiter.jsx'
18 18
 import CardPopup from './component/CardPopup/CardPopup.jsx'
19 19
 import CardPopupCreateContent from './component/CardPopup/CardPopupCreateContent.jsx'
20 20
 
21
+import { libGenerateAvatarFromPublicName } from './helper.js'
21 22
 
22 23
 import NewVersionButton from './component/OptionComponent/NewVersionBtn.jsx'
23 24
 import ArchiveDeleteContent from './component/OptionComponent/ArchiveDeleteContent.jsx'
@@ -98,7 +99,18 @@ ReactDOM.render(
98 99
             email: 'osef@algoo.fr',
99 100
             avatar_url: 'https://avatars3.githubusercontent.com/u/11177014?s=460&v=4'
100 101
           }}
101
-          timelineData={TimelineDebugData}
102
+          timelineData={TimelineDebugData.map(item => item.timelineType === 'comment'
103
+            ? {
104
+              ...item,
105
+              author: {
106
+                ...item.author,
107
+                avatar_url: item.author.avatar_url
108
+                  ? item.author.avatar_url
109
+                  : libGenerateAvatarFromPublicName(item.author.public_name)
110
+              }
111
+            }
112
+            : item
113
+          )}
102 114
           newComment={''}
103 115
           disableComment={false}
104 116
           wysiwyg={false}

+ 3 - 2
frontend_lib/src/index.js View File

@@ -1,6 +1,7 @@
1 1
 import {
2 2
   libAddAllResourceI18n,
3
-  libHandleFetchResult
3
+  libHandleFetchResult,
4
+  libGenerateAvatarFromPublicName
4 5
 } from './helper.js'
5 6
 
6 7
 import libPopinFixed from './component/PopinFixed/PopinFixed.jsx'
@@ -28,8 +29,8 @@ import libArchiveDeleteContent from './component/OptionComponent/ArchiveDeleteCo
28 29
 import libSelectStatus from './component/Input/SelectStatus/SelectStatus.jsx'
29 30
 
30 31
 export const addAllResourceI18n = libAddAllResourceI18n
31
-
32 32
 export const handleFetchResult = libHandleFetchResult
33
+export const generateAvatarFromPublicName = libGenerateAvatarFromPublicName
33 34
 
34 35
 export const PopinFixed = libPopinFixed
35 36
 export const PopinFixedHeader = libPopinFixedHeader