diff --git a/.eslintignore b/.eslintignore
index 3a259c106..9ed3e5361 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -13,4 +13,5 @@ plugins/*
!plugins/coral-plugin-viewing-options
!plugins/coral-plugin-comment-content
!plugins/talk-plugin-permalink
+!plugins/talk-plugin-avatar
node_modules
diff --git a/.gitignore b/.gitignore
index 0be1266c6..e94a01a6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,5 +26,6 @@ plugins/*
!plugins/coral-plugin-viewing-options
!plugins/coral-plugin-comment-content
!plugins/talk-plugin-permalink
+!plugins/talk-plugin-avatar
**/node_modules/*
diff --git a/client/coral-embed-stream/src/containers/Comment.js b/client/coral-embed-stream/src/containers/Comment.js
index 040d1a83e..1ffac82cf 100644
--- a/client/coral-embed-stream/src/containers/Comment.js
+++ b/client/coral-embed-stream/src/containers/Comment.js
@@ -10,7 +10,8 @@ const pluginFragments = getSlotsFragments([
'commentInfoBar',
'commentActions',
'commentContent',
- 'commentReactions'
+ 'commentReactions',
+ 'commentAvatar'
]);
export default withFragments({
diff --git a/plugin-api/beta/client/hocs/index.js b/plugin-api/beta/client/hocs/index.js
index 7be682670..7f3476cfd 100644
--- a/plugin-api/beta/client/hocs/index.js
+++ b/plugin-api/beta/client/hocs/index.js
@@ -1 +1,2 @@
export {default as withReaction} from './withReaction';
+export {default as withFragments} from 'coral-framework/hocs/withFragments';
diff --git a/plugins/talk-plugin-avatar/client/.babelrc b/plugins/talk-plugin-avatar/client/.babelrc
new file mode 100644
index 000000000..60be246eb
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/.babelrc
@@ -0,0 +1,14 @@
+{
+ "presets": [
+ "es2015"
+ ],
+ "plugins": [
+ "add-module-exports",
+ "transform-class-properties",
+ "transform-decorators-legacy",
+ "transform-object-assign",
+ "transform-object-rest-spread",
+ "transform-async-to-generator",
+ "transform-react-jsx"
+ ]
+}
\ No newline at end of file
diff --git a/plugins/talk-plugin-avatar/client/.eslintrc.json b/plugins/talk-plugin-avatar/client/.eslintrc.json
new file mode 100644
index 000000000..9fe56bd14
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/.eslintrc.json
@@ -0,0 +1,23 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "mocha": true
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true,
+ "jsx": true
+ }
+ },
+ "parser": "babel-eslint",
+ "plugins": [
+ "react"
+ ],
+ "rules": {
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
+ }
+}
diff --git a/plugins/talk-plugin-avatar/client/assets/avatar-placeholder.png b/plugins/talk-plugin-avatar/client/assets/avatar-placeholder.png
new file mode 100644
index 000000000..2e828f0fb
Binary files /dev/null and b/plugins/talk-plugin-avatar/client/assets/avatar-placeholder.png differ
diff --git a/plugins/talk-plugin-avatar/client/components/UserAvatar.js b/plugins/talk-plugin-avatar/client/components/UserAvatar.js
new file mode 100644
index 000000000..ae1ab30f9
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/components/UserAvatar.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import styles from './styles.css';
+import avatarPlaceholder from '../assets/avatar-placeholder.png';
+
+const UserAvatar = ({comment: {user}}) =>
+
+
+export default UserAvatar;
diff --git a/plugins/talk-plugin-avatar/client/components/styles.css b/plugins/talk-plugin-avatar/client/components/styles.css
new file mode 100644
index 000000000..fc6111845
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/components/styles.css
@@ -0,0 +1,4 @@
+.avatarPlaceholder {
+ height: 50px;
+ width: 50px;
+}
\ No newline at end of file
diff --git a/plugins/talk-plugin-avatar/client/containers/UserAvatar.js b/plugins/talk-plugin-avatar/client/containers/UserAvatar.js
new file mode 100644
index 000000000..76b558e05
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/containers/UserAvatar.js
@@ -0,0 +1,12 @@
+import {gql} from 'react-apollo';
+import {withFragments} from 'plugin-api/beta/client/hocs';
+import UserAvatar from '../components/UserAvatar';
+
+export default withFragments({
+ comment: gql`
+ fragment UserAvatar_comment on Comment {
+ user {
+ avatar
+ }
+ }`
+})(UserAvatar);
diff --git a/plugins/talk-plugin-avatar/client/index.js b/plugins/talk-plugin-avatar/client/index.js
new file mode 100644
index 000000000..922e464b8
--- /dev/null
+++ b/plugins/talk-plugin-avatar/client/index.js
@@ -0,0 +1,7 @@
+import UserAvatar from './components/UserAvatar';
+
+export default {
+ slots: {
+ commentAvatar: [UserAvatar]
+ }
+}
\ No newline at end of file
diff --git a/plugins/talk-plugin-avatar/index.js b/plugins/talk-plugin-avatar/index.js
new file mode 100644
index 000000000..b91a58835
--- /dev/null
+++ b/plugins/talk-plugin-avatar/index.js
@@ -0,0 +1,75 @@
+// We need the UserModel because we need to update the user.
+const UserModel = require('models/user');
+
+// Get some middleware to use with the webhook.
+const auth = require('middleware/authentication');
+const authz = require('middleware/authorization');
+
+// Load some config from the environment. This could be changed to a settings
+// option later if you want to go that route.
+const DEFAULT_AVATAR = process.env.DEFAULT_AVATAR;
+
+module.exports = {
+
+ // The new type definitions provides the new "avatar" field needed to inject
+ // into the User type.
+ typeDefs: `
+ type User {
+ avatar: String
+ }
+ `,
+
+ // The User resolver will return the avatar from the embedded user metadata.
+ resolvers: {
+ User: {
+ avatar(user) {
+ if (user && user.metadata && user.metadata.avatar) {
+ return user.metadata.avatar;
+ }
+
+ return DEFAULT_AVATAR;
+ }
+ }
+ },
+
+ // The custom router routes that we add here will allow an external system to
+ // update the avatar when it changes on the remote system. Note that we do
+ // use the auth/authz middleware, checking for the ADMIN role. This can be
+ // used in conjunction with a personal access token generated from an ADMIN.
+ router(router) {
+ router.post('/webhooks/user_update', auth, authz.needed('ADMIN'), async (req, res, next) => {
+
+ // We expect that the payload for the new avatar is in the following form:
+ //
+ // {
+ // "id": "123123-123123-12312313",
+ // "avatar": "https://great-cdn.cloudfront.net/best-photo.jpg"
+ // ...
+ // }
+
+
+ // Extract the data from the payload.
+ let {
+ id,
+ avatar
+ } = req.body;
+
+ try {
+
+ // Update the user model.
+ await UserModel.update({id}, {
+ $set: {
+ 'metadata.avatar': avatar
+ }
+ });
+
+ } catch (e) {
+ return next(e);
+ }
+
+ // Respond with a `202 Accepted` to indicate that we were able to process
+ // the update.
+ res.status(202).end()
+ });
+ }
+};
\ No newline at end of file
diff --git a/plugins/talk-plugin-avatar/package.json b/plugins/talk-plugin-avatar/package.json
new file mode 100644
index 000000000..c652a86d0
--- /dev/null
+++ b/plugins/talk-plugin-avatar/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "talk-plugin-avatar",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "Belen Curcio ",
+ "license": "ISC"
+}