0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Colorized avatars

This commit is contained in:
Peter Zimon 2022-07-07 12:39:18 +02:00
parent 6948b1222b
commit ff362cabbf
7 changed files with 59 additions and 13 deletions

View file

@ -23,13 +23,13 @@ function AuthFrame({adminUrl, onLoad}) {
);
}
function CommentsBoxContainer({done, colorScheme}) {
function CommentsBoxContainer({done, colorScheme, avatarSaturation}) {
if (!done) {
return null;
}
return (
<ShadowRoot>
<CommentsBox colorScheme={colorScheme} />
<CommentsBox colorScheme={colorScheme} avatarSaturation={avatarSaturation} />
</ShadowRoot>
);
}
@ -297,7 +297,7 @@ export default class App extends React.Component {
return (
<SentryErrorBoundary dsn={this.props.sentryDsn}>
<AppContext.Provider value={this.getContextFromState()}>
<CommentsBoxContainer done={done} colorScheme={this.props.colorScheme} />
<CommentsBoxContainer done={done} colorScheme={this.props.colorScheme} avatarSaturation={this.props.avatarSaturation} />
<AuthFrame adminUrl={this.props.adminUrl} onLoad={this.initSetup.bind(this)}/>
</AppContext.Provider>
</SentryErrorBoundary>

View file

@ -58,7 +58,7 @@ const AddForm = (props) => {
<div className="flex mb-4 space-x-4 justify-start items-center">
<Avatar />
<div>
<h4 className="text-lg font-sans font-bold mb-1 tracking-tight dark:text-neutral-300">{member.name}</h4>
<h4 className="text-lg font-sans font-bold mb-1 tracking-tight dark:text-neutral-300">{member.name ? member.name : 'Anonymous'}</h4>
<h6 className="text-[13px] text-neutral-400 font-sans">Now</h6>
</div>
</div>

View file

@ -4,12 +4,50 @@ import {getInitials} from '../utils/helpers';
class Avatar extends React.Component {
static contextType = AppContext;
getHashOfString(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
hash = Math.abs(hash);
return hash;
}
normalizeHash(hash, min, max) {
return Math.floor((hash % (max - min)) + min);
}
generateHSL() {
let commentMember = (this.props.comment ? this.props.comment.member : this.context.member);
if (!commentMember || !commentMember.name) {
return [0,0,10];
}
const saturation = this.props.saturation ? this.props.saturation : 50;
const hRange = [0, 360];
const lRangeTop = Math.round(saturation / (100 / 30)) + 20;
const lRangeBottom = lRangeTop - 10;
const lRange = [lRangeBottom, lRangeTop];
const hash = this.getHashOfString(commentMember.name);
const h = this.normalizeHash(hash, hRange[0], hRange[1]);
const l = this.normalizeHash(hash, lRange[0], lRange[1]);
return [h, saturation, l];
}
HSLtoString(hsl) {
return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
}
getInitials() {
let commentMember = (this.props.comment ? this.props.comment.member : this.context.member);
if (!commentMember || !commentMember.name) {
return '';
return getInitials('Anonymous');
}
return getInitials(commentMember.name);
}
@ -19,9 +57,14 @@ class Avatar extends React.Component {
let initialsClasses = (this.props.size === 'small' ? 'text-sm' : 'text-base');
let commentMember = (this.props.comment ? this.props.comment.member : this.context.member);
const bgColor = this.HSLtoString(this.generateHSL());
const avatarStyle = {
background: bgColor
};
return (
<figure className={`relative ${dimensionClasses}`}>
<div className={`flex justify-center items-center rounded-full bg-black ${dimensionClasses}`}>
<div className={`flex justify-center items-center rounded-full ${dimensionClasses}`} style={avatarStyle}>
<p className={`text-white font-sans font-semibold ${initialsClasses}`}>{ this.getInitials() }</p>
</div>
<img className={`absolute top-0 left-0 rounded-full ${dimensionClasses}`} src={commentMember.avatar_image} alt="Avatar"/>

View file

@ -49,7 +49,7 @@ const Comment = (props) => {
<div className={`flex flex-col ${commentMarginBottom}`}>
<div>
<div className="flex mb-4 space-x-4 justify-start items-center">
<Avatar comment={comment} />
<Avatar comment={comment} saturation={props.avatarSaturation} />
<div>
<h4 className="text-lg font-sans font-bold mb-1 tracking-tight dark:text-neutral-300">{comment.member.name}</h4>
<h6 className="text-[13px] text-neutral-400 font-sans">{formatRelativeTime(comment.created_at)}</h6>
@ -66,7 +66,7 @@ const Comment = (props) => {
</div>
{hasReplies &&
<div className="ml-14 mt-14">
<Replies comment={comment} />
<Replies comment={comment} avatarSaturation={props.avatarSaturation} />
</div>
}
{isInReplyMode &&

View file

@ -47,7 +47,7 @@ class CommentsBox extends React.Component {
}
render() {
const comments = !this.context.comments ? 'Loading...' : this.context.comments.slice().reverse().map(comment => <Comment comment={comment} key={comment.id} />);
const comments = !this.context.comments ? 'Loading...' : this.context.comments.slice().reverse().map(comment => <Comment comment={comment} key={comment.id} avatarSaturation={this.props.avatarSaturation} />);
const containerClass = this.darkMode() ? 'dark' : '';

View file

@ -5,7 +5,7 @@ const Replies = (props) => {
return (
<div>
{comment.replies.map((reply => <Comment comment={reply} parent={comment} key={reply.id} isReply={true} />))}
{comment.replies.map((reply => <Comment comment={reply} parent={comment} key={reply.id} isReply={true} avatarSaturation={props.avatarSaturation} />))}
</div>
);
};

View file

@ -35,7 +35,9 @@ function getSiteData() {
const sentryDsn = scriptTag.dataset.sentryDsn;
const postId = scriptTag.dataset.postId;
const colorScheme = scriptTag.dataset.colorScheme;
return {siteUrl, apiKey, apiUrl, sentryDsn, postId, adminUrl, colorScheme};
const avatarSaturation = scriptTag.dataset.avatarSaturation;
return {siteUrl, apiKey, apiUrl, sentryDsn, postId, adminUrl, colorScheme, avatarSaturation};
}
return {};
}
@ -55,12 +57,13 @@ function setup({siteUrl}) {
function init() {
// const customSiteUrl = getSiteUrl();
const {siteUrl: customSiteUrl, sentryDsn, postId, adminUrl, colorScheme} = getSiteData();
const {siteUrl: customSiteUrl, sentryDsn, postId, adminUrl, colorScheme, avatarSaturation} = getSiteData();
const siteUrl = customSiteUrl || window.location.origin;
setup({siteUrl});
ReactDOM.render(
<React.StrictMode>
{<App adminUrl={adminUrl} siteUrl={siteUrl} customSiteUrl={customSiteUrl} sentryDsn={sentryDsn} postId={postId} colorScheme={colorScheme} />}
{<App adminUrl={adminUrl} siteUrl={siteUrl} customSiteUrl={customSiteUrl} sentryDsn={sentryDsn} postId={postId} colorScheme={colorScheme} avatarSaturation={avatarSaturation} />}
</React.StrictMode>,
document.getElementById(ROOT_DIV_ID)
);