From dcf0561ceec6b8e241b393ad1f68f702761ceb0e Mon Sep 17 00:00:00 2001 From: Sacha Greif Date: Sun, 22 May 2016 15:23:12 +0900 Subject: [PATCH] do not use meteor-avatar-core after all because of client/server npm dependencies issues --- packages/nova-base-components/lib/config.js | 3 +- .../lib/users/UsersAvatar.jsx | 5 +- packages/nova-core/lib/avatar.js | 279 ++++++++++++++++++ packages/nova-core/lib/core.js | 3 +- 4 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 packages/nova-core/lib/avatar.js diff --git a/packages/nova-base-components/lib/config.js b/packages/nova-base-components/lib/config.js index 50d55d59b..36ed89d12 100644 --- a/packages/nova-base-components/lib/config.js +++ b/packages/nova-base-components/lib/config.js @@ -1,4 +1,5 @@ -import Avatar from 'meteor-avatar-core'; +// import Avatar from 'meteor-avatar-core'; +import { Avatar } from 'meteor/nova:core'; // import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; // checkNpmVersions({ diff --git a/packages/nova-base-components/lib/users/UsersAvatar.jsx b/packages/nova-base-components/lib/users/UsersAvatar.jsx index a96c64fd9..db5856da1 100644 --- a/packages/nova-base-components/lib/users/UsersAvatar.jsx +++ b/packages/nova-base-components/lib/users/UsersAvatar.jsx @@ -1,5 +1,8 @@ import React, { PropTypes, Component } from 'react'; -import Avatar from 'meteor-avatar-core'; +// import Avatar from 'meteor-avatar-core'; +import { Avatar } from 'meteor/nova:core'; + +console.log(Avatar) const UsersAvatar = ({user, size, link}) => { diff --git a/packages/nova-core/lib/avatar.js b/packages/nova-core/lib/avatar.js new file mode 100644 index 000000000..d68423a38 --- /dev/null +++ b/packages/nova-core/lib/avatar.js @@ -0,0 +1,279 @@ +// var _ = require('underscore'); + +// import Gravatar from 'gravatar'; + +var Avatar = { + + // Default functionality. You can override these options by calling + // Avater.setOptions (do not set this.options directly) + + options: { + + // Determines the type of fallback to use when no image can be found via + // linked services (Gravatar included): + // 'default image' (the default option, which will show either the image + // specified by defaultImageUrl, the package's default image, or a Gravatar + // default image) + // OR + // 'initials' (show the user's initials). + fallbackType: '', + + // This will replace the included default avatar image's URL + // ('packages/utilities_avatar/default.png'). It can be a relative path + // (relative to website's base URL, e.g. 'images/defaultthis.png'). + defaultImageUrl: 'https://placekitten.com/80/80', + + // This property name will be used to fetch an avatar url from the user's profile + // (e.g. 'avatar'). If this property is set and a property of that name exists + // on the user's profile (e.g. user.profile.avatar) that property will be used + // as the avatar url. + customImageProperty: '', + + // Gravatar default option to use (overrides default image URL) + // Options are available at: + // https://secure.gravatar.com/site/implement/images/#default-image + gravatarDefault: '', + + // This property is used to prefix the CSS classes of the DOM elements. + // If no value is set, then the default CSS class assigned to all DOM elements are prefixed with 'avatar' as default. + // If a value is set to, 'foo' for example, the resulting CSS classes are prefixed with 'foo'. + cssClassPrefix: '', + + // This property defines the various image sizes available + imageSizes: { + 'large': 80, + 'small': 30, + 'extra-small': 20 + }, + + // Default background color when displaying the initials. + // Can also be set to a function to map an user object to a background color. + backgroundColor: '#aaa', + + // Default text color when displaying the initials. + // Can also be set to a function to map an user object to a text color. + textColor: '#fff', + + // Generate the required CSS and include it in the head of your application. + // Setting this to false will exclude the generated CSS and leave the + // avatar unstyled by the package. + generateCSS: true + }, + + // Sets the Avatar options. You must use this setter function rather than assigning directly to + // this.options, otherwise the stylesheet won't be generated. + + setOptions: function(options) { + this.options = _.extend(this.options, options); + }, + + // Returns the cssClassPrefix property from options + getCssClassPrefix: function () { + return this.options.cssClassPrefix ? this.options.cssClassPrefix : 'avatar'; + }, + + // Returns a background color for initials + getBackgroundColor: function (user) { + if (_.isString(this.options.backgroundColor)) + return this.options.backgroundColor; + else if (_.isFunction(this.options.backgroundColor)) + return this.options.backgroundColor(user); + }, + + // Returns a text color for initials + getTextColor: function (user) { + if (_.isString(this.options.textColor)) + return this.options.textColor; + else if (_.isFunction(this.options.textColor)) + return this.options.textColor(user); + }, + + // Get the initials of the user + getInitials: function (user) { + + var initials = ''; + var name = ''; + var parts = []; + + if (user && user.profile && user.profile.firstName) { + initials = user.profile.firstName.charAt(0).toUpperCase(); + + if (user.profile.lastName) { + initials += user.profile.lastName.charAt(0).toUpperCase(); + } + else if (user.profile.familyName) { + initials += user.profile.familyName.charAt(0).toUpperCase(); + } + else if (user.profile.secondName) { + initials += user.profile.secondName.charAt(0).toUpperCase(); + } + } + else { + if (user && user.profile && user.profile.name) { + name = user.profile.name; + } + else if (user && user.username) { + name = user.username; + } + + parts = name.split(' '); + // Limit getInitials to first and last initial to avoid problems with + // very long multi-part names (e.g. 'Jose Manuel Garcia Galvez') + initials = _.first(parts).charAt(0).toUpperCase(); + if (parts.length > 1) { + initials += _.last(parts).charAt(0).toUpperCase(); + } + } + + return initials; + }, + + // Get the url of the user's avatar + // XXX: this.getUrl is a reactive function only when no user argument is specified. + getUrl: function (user) { + + // Default to the currently logged in user, unless otherwise specified. + if (!user) return null; + + var url = ''; + var defaultUrl, svc; + + if (user) { + svc = this.getService(user); + if (svc === 'twitter') { + // use larger image (200x200 is smallest custom option) + url = user.services.twitter.profile_image_url_https.replace('_normal.', '_200x200.'); + } + else if (svc === 'facebook') { + // use larger image (~200x200) + url = 'https://graph.facebook.com/' + user.services.facebook.id + '/picture/?type=large'; + } + else if (svc === 'google') { + url = user.services.google.picture; + } + else if (svc === 'github') { + url = 'https://avatars.githubusercontent.com/' + user.services.github.username + '?s=200'; + } + else if (svc === 'instagram') { + url = user.services.instagram.profile_picture; + } + else if (svc === 'linkedin') { + url = user.services.linkedin.pictureUrl; + } + else if (svc === 'custom') { + url = this.getCustomUrl(user); + } + else if (svc === 'none') { + defaultUrl = this.options.defaultImageUrl || '/packages/utilities_avatar/default.png'; + // If it's a relative path (no '//' anywhere), complete the URL + if (defaultUrl.indexOf('//') === -1) { + // Add starting slash if it does not exist + if (defaultUrl.charAt(0) !== '/') defaultUrl = '/' + defaultUrl; + // Then add the relative path to the server's base URL + defaultUrl = [window.location.origin, defaultUrl].join(''); + } + url = this.getGravatarUrl(user, defaultUrl); + } + } + + return url; + }, + + getService: function (user) { + var services = user && user.services || {}; + if (this.getCustomUrl(user)) { return 'custom'; } + var service = _.find([['twitter', 'profile_image_url_https'], ['facebook', 'id'], ['google', 'picture'], ['github', 'username'], ['instagram', 'profile_picture'], ['linkedin', 'pictureUrl']], function(s) { return !!services[s[0]] && s[1].length && !!services[s[0]][s[1]]; }); + if(!service) + return 'none'; + else + return service[0]; + }, + + computeUrl: function(prop) { + if (typeof prop === 'function') { + prop = prop.call(user); + } + if (prop && typeof prop === 'string') { + return prop; + } + }, + + getDescendantProp: function (obj, desc) { + var arr = desc.split('.'); + while(arr.length && (obj = obj[arr.shift()])); + return obj; + }, + + getCustomUrl: function (user) { + + var customProp = user && this.options.customImageProperty; + if (typeof customProp === 'function') { + return this.computeUrl(customProp); + } else if (customProp) { + return this.computeUrl(this.getDescendantProp(user, customProp)); + } + }, + + getGravatarUrl: function (user, defaultUrl) { + var gravatarDefault; + var validGravatars = ['404', 'mm', 'identicon', 'monsterid', 'wavatar', 'retro', 'blank']; + + // Initials are shown when Gravatar returns 404. + if (this.options.fallbackType !== 'initials') { + var valid = _.contains(validGravatars, this.options.gravatarDefault); + gravatarDefault = valid ? this.options.gravatarDefault : defaultUrl; + } + else { + gravatarDefault = '404'; + } + + var email = this.getUserEmail(user); + var secure = true; + var options = { + // NOTE: Gravatar's default option requires a publicly accessible URL, + // so it won't work when your app is running on localhost and you're + // using an image with either the standard default image URL or a custom + // defaultImageUrl that is a relative path (e.g. 'images/defaultthis.png'). + size: 200, // use 200x200 like twitter and facebook above (might be useful later) + default: gravatarDefault, + secure: true + }; + return email ? Gravatar.imageUrl(email, options) : null; + + }, + + // Get the user's email address + getUserEmail: function (user) { + var emails = _.pluck(user.emails, 'address'); + return emails[0] || null; + }, + + // Returns the size class to use for an avatar + getSizeClass: function(context) { + // Defaults are 'large', 'small', 'extra-small', but user can add new ones + return this.options.imageSizes[context.size] ? this.getCssClassPrefix() + '-' + context.size : ''; + }, + + // Returns the shape class for an avatar + getShapeClass: function (context) { + var valid = ['rounded', 'circle']; + return _.contains(valid, context.shape) ? this.getCssClassPrefix() + '-' + context.shape : ''; + }, + + // Returns the custom class(es) for an avatar + getCustomClasses: function (context) { + return context.class ? context.class : ''; + }, + + // Returns the initials text for an avatar + getInitialsText: function(user, context) { + return context.initials || this.getInitials(user); + } + +}; + +// This will be replaced if the user calls setOptions in their own code +Avatar.setOptions({}); + +module.exports = Avatar; +export default Avatar; \ No newline at end of file diff --git a/packages/nova-core/lib/core.js b/packages/nova-core/lib/core.js index 06c29dc2d..34da26d21 100644 --- a/packages/nova-core/lib/core.js +++ b/packages/nova-core/lib/core.js @@ -4,5 +4,6 @@ import ContextPasser from "./components/ContextPasser.jsx"; import FlashContainer from "./containers/FlashContainer.jsx"; import AppComposer from "./containers/AppComposer.jsx"; import CurrentUserContainer from "./containers/CurrentUserContainer.jsx"; +import Avatar from "./avatar.js"; -export default {Messages, ModalTrigger, ContextPasser, AppComposer, FlashContainer, CurrentUserContainer}; \ No newline at end of file +export { Avatar, Messages, ModalTrigger, ContextPasser, AppComposer, FlashContainer, CurrentUserContainer}; \ No newline at end of file