Skip to content

Commit

Permalink
Add avatar image support (#1486)
Browse files Browse the repository at this point in the history
* Add avatar image support and move avatar initials props to styleOptions

* Add sample

* Update PR number

* Remove memoize style

* Update verbiage

* Using CroppedImage for centering
  • Loading branch information
compulim authored Dec 14, 2018
1 parent b21673e commit d35a7ac
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 56 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

<!-- CHANGELOG line template
### Added/Changed/Removed
- Added something, by [@johndoe](https://github.com/johndoe) in PR [#XXX](https://github.com/Microsoft/BotFramework-WebChat/pull/XXX)
- Added something, by [@johndoe](https://github.com/johndoe), in PR [#XXX](https://github.com/Microsoft/BotFramework-WebChat/pull/XXX)
### Fixed
- Fix [#XXX](https://github.com/Microsoft/BotFramework-WebChat/issues/XXX). Patched something, by [@johndoe](https://github.com/johndoe) in PR [#XXX](https://github.com/Microsoft/BotFramework-WebChat/pull/XXX)
Expand All @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Added
- Fix [#1383](https://github.com/Microsoft/BotFramework-WebChat/issues/1383). Added options to hide upload button, by [@compulim](https://github.com/compulim) in PR [#1491](https://github.com/Microsoft/BotFramework-WebChat/pull/1491)
- Added support of avatar image, thru `styleOptions.botAvatarImage` and `styleOptions.userAvatarImage`, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)

### Changed
- Moved `botAvatarImage` and `userAvatarImage` to `styleOptions.botAvatarImage` and `styleOptions.userAvatarImage` respectively, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)

### Fixed
- Fix [#1360](https://github.com/Microsoft/BotFramework-WebChat/issues/1360). Added `roles` to components of Web Chat, by [@corinagum](https://github.com/corinagum) in PR [#1462](https://github.com/Microsoft/BotFramework-WebChat/pull/1462)
Expand All @@ -25,6 +29,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Samples
- UI: [Hide upload button](https://microsoft.github.io/BotFramework-WebChat/05.d.hide-upload-button-styling/), in [#1491](https://github.com/Microsoft/BotFramework-WebChat/pull/1491)

### Removed
- `botAvatarImage` and `userAvatarImage` props, as they are moved inside `styleOptions`, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)

### Samples
- UI: [Avatar image](https://microsoft.github.io/BotFramework-WebChat/04.b.display-user-bot-images-styling/), in [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)

## [4.2.0] - 2018-12-11
### Added
- Build: Development build now include instrumentation code, updated build scripts
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ npm run prepublishOnly
| [`03.a.host-with-react`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/03.a.host-with-react) | Demonstrates how to create a React component that hosts the full-featured Web Chat. | [Demo](https://microsoft.github.io/BotFramework-WebChat/03.a.host-with-react) |
| [`03.b.host-with-Angular5`](https://github.com/Microsoft/BotFramework-WebChat/issues/1423) | Demonstrates how to create an Angular component that hosts the full-featured Web Chat. | |
| [`04.display-user-bot-initials-styling`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/04.display-user-bot-initials-styling) | Demonstrates how to display initials for both Web Chat participants. | [Demo](https://microsoft.github.io/BotFramework-WebChat/04.display-user-bot-initials-styling) |
| [`04.b.display-user-bot-images-styling`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/04.b.display-user-bot-images-styling) | Demonstrates how to display images and initials for both Web Chat participants. | [Demo](https://microsoft.github.io/BotFramework-WebChat/04.b.display-user-bot-images-styling) |
| [`05.a.branding-webchat-styling`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/05.a.branding-webchat-styling) | Introduces the ability to style Web Chat to match your brand. This method of custom styling will not break upon Web Chat updates. | [Demo](https://microsoft.github.io/BotFramework-WebChat/05.a.branding-webchat-styling) |
| [`05.b.idiosyncratic-manual-styling`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/05.b.idiosyncratic-manual-styling/) | Demonstrates how to make manual style changes, and is a more complicated and time-consuming way to customize styling of Web Chat. Manual styles may be broken upon Web Chat updates. | [Demo](https://microsoft.github.io/BotFramework-WebChat/05.b.idiosyncratic-manual-styling) |
| [`05.c.presentation-mode-styling`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/05.c.presentation-mode-styling) | Demonstrates how to set up Presentation Mode, which displays chat history but does not show the send box, and disables the interactivity of Adaptive Cards. | [Demo](https://microsoft.github.io/BotFramework-WebChat/05.c.presentation-mode-styling) |
Expand Down
54 changes: 44 additions & 10 deletions packages/component/src/Activity/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,57 @@ import classNames from 'classnames';
import React from 'react';

import connectToWebChat from '../connectToWebChat';
import CroppedImage from '../Utils/CroppedImage';

export default connectToWebChat(
({ styleSet }) => ({ styleSet })
)(
const connectAvatar = (...selectors) => connectToWebChat(
({
children,
className,
fromUser,
styleSet
}) =>
styleSet: {
options: {
botAvatarImage,
botAvatarInitials,
userAvatarImage,
userAvatarInitials
}
}
}, { fromUser }) => ({
avatarImage: fromUser ? userAvatarImage : botAvatarImage,
avatarInitials: fromUser ? userAvatarInitials : botAvatarInitials
}),
...selectors
);

// TODO: [P2] Consider memoizing "style={ backgroundImage }" in our upstreamers
// We have 2 different upstreamers <CarouselFilmStrip> and <StackedLayout>

const Avatar = ({
avatarImage,
avatarInitials,
className,
fromUser,
styleSet
}) =>
!!(avatarImage || avatarInitials) &&
<div
className={ classNames(
styleSet.avatar + '',
{ 'from-user': fromUser },
(className || '') + ''
) }
>
{ children }
{ avatarInitials }
{ !!avatarImage &&
<CroppedImage
alt=""
className="image"
height="100%"
src={ avatarImage }
width="100%"
/>
}
</div>
)

export default connectAvatar(
({ styleSet }) => ({ styleSet })
)(Avatar)

export { connectAvatar }
21 changes: 4 additions & 17 deletions packages/component/src/Activity/CarouselFilmStrip.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,7 @@ const ROOT_CSS = css({
});

const connectCarouselFilmStrip = (...selectors) => connectToWebChat(
({
botAvatarInitials,
language,
userAvatarInitials
}) => ({
botAvatarInitials,
language,
userAvatarInitials
}),
({ language }) => ({ language }),
...selectors
)

Expand All @@ -79,16 +71,13 @@ const ConnectedCarouselFilmStrip = connectCarouselFilmStrip(
)(
({
activity,
botAvatarInitials,
children,
className,
filmContext,
showTimestamp,
styleSet,
userAvatarInitials
}) => {
const fromUser = activity.from.role === 'user';
const initials = fromUser ? userAvatarInitials : botAvatarInitials;

return (
<div
Expand All @@ -99,16 +88,14 @@ const ConnectedCarouselFilmStrip = connectCarouselFilmStrip(
) }
ref={ filmContext._setFilmStripRef }
>
{ !!initials &&
<Avatar className="avatar" fromUser={ fromUser }>{ initials }</Avatar>
}
<Avatar className="avatar" fromUser={ fromUser } />
<div className="content">
{
!!activity.text &&
<div className="message">
<Bubble
className="bubble"
fromUser={ activity.from.role === 'user' }
fromUser={ fromUser }
>
{ children({
activity,
Expand All @@ -126,7 +113,7 @@ const ConnectedCarouselFilmStrip = connectCarouselFilmStrip(
activity.attachments.map((attachment, index) =>
<li key={ index }>
<Bubble
fromUser={ activity.from.role === 'user' }
fromUser={ fromUser }
key={ index }
>
{ children({ attachment }) }
Expand Down
12 changes: 5 additions & 7 deletions packages/component/src/Activity/StackedLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,11 @@ export default connectStackedLayout(
)(
({
activity,
botAvatarInitials,
children,
showTimestamp,
styleSet,
userAvatarInitials
styleSet
}) => {
const fromUser = activity.from.role === 'user';
const initials = fromUser ? userAvatarInitials : botAvatarInitials;
const { state } = activity.channelData || {};
const showSendStatus = state === SENDING || state === SEND_FAILED;

Expand All @@ -90,9 +87,10 @@ export default connectStackedLayout(
{ 'from-user': fromUser }
) }
>
{ !!initials &&
<Avatar className="avatar" fromUser={ fromUser }>{ initials }</Avatar>
}
<Avatar
className="avatar"
fromUser={ fromUser }
/>
<div className="content">
{
activity.type === 'typing' ?
Expand Down
36 changes: 28 additions & 8 deletions packages/component/src/Composer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Composer as ScrollToBottomComposer,
FunctionContext as ScrollToBottomFunctionContext
} from 'react-scroll-to-bottom';

import { connect } from 'react-redux';
import { css } from 'glamor';
import memoize from 'memoize-one';
Expand Down Expand Up @@ -133,12 +134,36 @@ function createFocusSendBoxLogic({ sendBoxRef }) {
};
}

function createStyleSetLogic({ styleSet, styleOptions }) {
function createStyleSetLogic({ styleOptions, styleSet }) {
return {
styleSet: styleSetToClassNames(styleSet || createStyleSet(styleOptions))
};
}

// TODO: [P3] Take this deprecation code out when releasing on or after 2019 December 11
function patchPropsForAvatarInitials({ botAvatarInitials, userAvatarInitials, ...props }) {
// This code will take out "botAvatarInitials" and "userAvatarInitials" from props

let { styleOptions } = props;

if (botAvatarInitials) {
styleOptions = { ...styleOptions, botAvatarInitials };

console.warn('Web Chat: "botAvatarInitials" is deprecated. Please use "styleOptions.botAvatarInitials" instead.');
}

if (userAvatarInitials) {
styleOptions = { ...styleOptions, userAvatarInitials };

console.warn('Web Chat: "userAvatarInitials" is deprecated. Please use "styleOptions.userAvatarInitials" instead.');
}

return {
...props,
styleOptions
};
}

function createLogic(props) {
// This is a heavy function, and it is expected to be only called when there is a need to recreate business logic, e.g.
// - User ID changed, causing all send* functions to be updated
Expand All @@ -151,6 +176,8 @@ function createLogic(props) {
// 2. Filter out profanity

// TODO: [P4] Revisit all members of context
props = patchPropsForAvatarInitials(props);

return {
...props,
...createCardActionLogic(props),
Expand Down Expand Up @@ -235,7 +262,6 @@ class Composer extends React.Component {
activityRenderer,
adaptiveCardHostConfig,
attachmentRenderer,
botAvatarInitials,
children,

// TODO: [P2] Add disable interactivity
Expand All @@ -247,7 +273,6 @@ class Composer extends React.Component {
renderMarkdown,
scrollToEnd,
store,
userAvatarInitials,
userID,
webSpeechPonyfillFactory,
...propsForLogic
Expand All @@ -268,15 +293,12 @@ class Composer extends React.Component {
adaptiveCardHostConfig: adaptiveCardHostConfig || defaultAdaptiveCardHostConfig(this.props.styleOptions),
attachmentRenderer,

// TODO: [P2] Move avatar initials to style options
botAvatarInitials,
groupTimestamp,
disabled,
grammars: grammars || EMPTY_ARRAY,
renderMarkdown,
scrollToEnd,
store,
userAvatarInitials,
webSpeechPonyfill: this.createWebSpeechPonyfill(webSpeechPonyfillFactory, referenceGrammarID)
}
);
Expand Down Expand Up @@ -337,7 +359,6 @@ ConnectedComposerWithStore.propTypes = {
activityRenderer: PropTypes.func,
adaptiveCardHostConfig: PropTypes.any,
attachmentRenderer: PropTypes.func,
botAvatarInitials: PropTypes.string,
groupTimestamp: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
disabled: PropTypes.bool,
grammars: PropTypes.arrayOf(PropTypes.string),
Expand All @@ -347,7 +368,6 @@ ConnectedComposerWithStore.propTypes = {
sendTimeout: PropTypes.number,
sendTyping: PropTypes.bool,
store: PropTypes.any,
userAvatarInitials: PropTypes.string,
userID: PropTypes.string,
webSpeechPonyfillFactory: PropTypes.func
};
9 changes: 8 additions & 1 deletion packages/component/src/Styles/StyleSet/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export default function createAvatarStyle({
height: avatarSize,
justifyContent: 'center',
overflow: 'hidden',
width: avatarSize
position: 'relative',
width: avatarSize,

'& > .image': {
left: 0,
position: 'absolute',
top: 0
}
};
}
27 changes: 17 additions & 10 deletions packages/component/src/Styles/defaultStyleSetOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,21 @@ const DEFAULT_ACCENT = '#0063B1';
const DEFAULT_SUBTLE = '#767676'; // With contrast 4.5:1 to white

const DEFAULT_OPTIONS = {
// Color and paddings
accent: DEFAULT_ACCENT,
avatarSize: 40,

backgroundColor: 'White',
paddingRegular: 10,
paddingWide: 20,
subtle: DEFAULT_SUBTLE,

// Avatar
avatarSize: 40,
botAvatarImage: '',
botAvatarInitials: '',
userAvatarImage: '',
userAvatarInitials: '',

// Bubble
bubbleBackground: 'White',
bubbleBorder: 'solid 1px #E6E6E6',
bubbleBorderRadius: 2,
Expand All @@ -21,26 +31,20 @@ const DEFAULT_OPTIONS = {
bubbleMinWidth: 250, // min screen width = 300px, Edge requires 372px (https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/13621468/)
bubbleTextColor: 'Black',

// Send box
hideSendBox: false,
hideUploadButton: false,

microphoneButtonColorOnDictate: '#F33',

paddingRegular: 10,
paddingWide: 20,

sendBoxButtonColor: '#767676',
sendBoxButtonColorOnDisabled: '#CCC',
sendBoxButtonColorOnFocus: '#333',
sendBoxButtonColorOnHover: '#333',

sendBoxHeight: 40,

// Visually show spoken text
showSpokenText: false,

subtle: DEFAULT_SUBTLE,

// Suggested actions
suggestedActionBackground: 'White',
suggestedActionBorder: `solid 2px ${ DEFAULT_ACCENT }`,
suggestedActionBorderRadius: 0,
Expand All @@ -50,15 +54,18 @@ const DEFAULT_OPTIONS = {
suggestedActionDisabledTextColor: DEFAULT_SUBTLE,
suggestedActionHeight: 40,

// Timestamp
timestampColor: DEFAULT_SUBTLE,

// Transcript overlay buttons (e.g. carousel and scroll to bottom)
transcriptOverlayButtonBackground: 'rgba(0, 0, 0, .6)',
transcriptOverlayButtonBackgroundOnFocus: 'rgba(0, 0, 0, .8)',
transcriptOverlayButtonBackgroundOnHover: 'rgba(0, 0, 0, .8)',
transcriptOverlayButtonColor: 'White',
transcriptOverlayButtonColorOnFocus: 'White',
transcriptOverlayButtonColorOnHover: 'White',

// Video
videoHeight: 270 // based on bubbleMaxWidth, 480 / 16 * 9 = 270
};

Expand Down
Loading

0 comments on commit d35a7ac

Please sign in to comment.