feat: stop using smiley default avatar, use first letter of name on colored square as fallback
This commit is contained in:
parent
76620c5e8f
commit
34042ede54
|
@ -71,7 +71,7 @@ class User extends mongoose.Model {
|
|||
email: this.email,
|
||||
token: this.generateJWT(),
|
||||
bio: this.bio,
|
||||
image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg',
|
||||
image: this.image,
|
||||
areTracksVisibleForAll: this.areTracksVisibleForAll,
|
||||
apiKey: this._id,
|
||||
};
|
||||
|
@ -81,7 +81,7 @@ class User extends mongoose.Model {
|
|||
return {
|
||||
username: this.username,
|
||||
bio: this.bio,
|
||||
image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg',
|
||||
image: this.image,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
html
|
||||
head
|
||||
base(href=baseUrl)
|
||||
title block title
|
||||
title
|
||||
block title
|
||||
| Authorization Server
|
||||
| - OpenBikeSensor Account
|
||||
|
||||
link(rel="stylesheet", href="/semantic-ui/semantic.min.css")
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
TracksPage,
|
||||
UploadPage,
|
||||
} from 'pages'
|
||||
import {LoginButton} from 'components'
|
||||
import {Avatar, LoginButton} from 'components'
|
||||
|
||||
const App = connect((state) => ({login: state.login}))(function App({login}) {
|
||||
return (
|
||||
|
@ -54,7 +54,7 @@ const App = connect((state) => ({login: state.login}))(function App({login}) {
|
|||
<>
|
||||
<li>
|
||||
<Link to="/settings">
|
||||
<Image src={login.image} avatar />
|
||||
<Avatar user={login} />
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
|
|
38
frontend/src/components/Avatar/index.tsx
Normal file
38
frontend/src/components/Avatar/index.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React from 'react'
|
||||
import {Comment} from 'semantic-ui-react'
|
||||
|
||||
import './styles.scss'
|
||||
|
||||
function hashCode(s) {
|
||||
let hash = 0
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
hash = (hash << 5) - hash + s.charCodeAt(i)
|
||||
hash |= 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
function getColor(s) {
|
||||
const h = Math.floor(hashCode(s)) % 360
|
||||
return `hsl(${h}, 50%, 50%)`
|
||||
}
|
||||
|
||||
export default function Avatar({user}) {
|
||||
const {image, username} = user || {}
|
||||
|
||||
if (image) {
|
||||
return <Comment.Avatar src={image} />
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
return <div className="avatar empty-avatar" />
|
||||
}
|
||||
|
||||
const color = getColor(username)
|
||||
|
||||
return (
|
||||
<div className="avatar text-avatar" style={{background: color}}>
|
||||
{username && <span>{username[0]}</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
43
frontend/src/components/Avatar/styles.scss
Normal file
43
frontend/src/components/Avatar/styles.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
.avatar.text-avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: -3px;
|
||||
|
||||
> span {
|
||||
color: white;
|
||||
font-size: 20pt;
|
||||
text-transform: uppercase;
|
||||
display: inline;
|
||||
|
||||
a:hover & {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.avatar.empty-avatar {
|
||||
background: #AAA;
|
||||
}
|
||||
|
||||
|
||||
.avatar.text-avatar,
|
||||
.avatar.empty-avatar {
|
||||
// border-radius: 0.25rem;
|
||||
border-radius: 10%;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
|
||||
.ui.comments & {
|
||||
display: inline-flex;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
}
|
||||
|
||||
.tiny.image & {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export {default as Avatar} from './Avatar'
|
||||
export {default as FileDrop} from './FileDrop'
|
||||
export {default as FileUploadField} from './FileUploadField'
|
||||
export {default as FormattedDate} from './FormattedDate'
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {Message, Segment, Form, Button, Loader, Header, Comment} from 'semantic-ui-react'
|
||||
import Markdown from 'react-markdown'
|
||||
|
||||
import {FormattedDate} from 'components'
|
||||
import {Avatar, FormattedDate} from 'components'
|
||||
|
||||
function CommentForm({onSubmit}) {
|
||||
const [body, setBody] = React.useState('')
|
||||
|
@ -32,7 +32,7 @@ export default function TrackComments({comments, onSubmit, onDelete, login, hide
|
|||
|
||||
{comments?.map((comment: TrackComment) => (
|
||||
<Comment key={comment.id}>
|
||||
<Comment.Avatar src={comment.author.image} />
|
||||
<Avatar user={comment.author} />
|
||||
<Comment.Content>
|
||||
<Comment.Author as="a">{comment.author.username}</Comment.Author>
|
||||
<Comment.Metadata>
|
||||
|
|
|
@ -8,7 +8,7 @@ import {map, switchMap, distinctUntilChanged} from 'rxjs/operators'
|
|||
import _ from 'lodash'
|
||||
|
||||
import type {Track} from 'types'
|
||||
import {Page, StripMarkdown} from 'components'
|
||||
import {Avatar, Page, StripMarkdown} from 'components'
|
||||
import api from 'api'
|
||||
import {useQueryParam} from 'query'
|
||||
|
||||
|
@ -99,7 +99,9 @@ function maxLength(t, max) {
|
|||
export function TrackListItem({track, privateTracks = false}) {
|
||||
return (
|
||||
<Item key={track.slug}>
|
||||
<Item.Image size="tiny" src={track.author.image} />
|
||||
<Item.Image size="tiny">
|
||||
<Avatar user={track.author} />
|
||||
</Item.Image>
|
||||
<Item.Content>
|
||||
<Item.Header as={Link} to={`/tracks/${track.slug}`}>
|
||||
{track.title || 'Unnamed track'}
|
||||
|
|
|
@ -2,7 +2,7 @@ import type {FeatureCollection, Point} from 'geojson'
|
|||
|
||||
export type UserProfile = {
|
||||
username: string
|
||||
image: string
|
||||
image?: string | null
|
||||
bio?: string | null
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue