Skip to content

Commit

Permalink
Merge branch 'master' into features/prevent-messages-removal
Browse files Browse the repository at this point in the history
* master:
  Rename image to avatars
  Create url for avatar download
  Translations to avatar
  Add social logins in avatar
  Improve design
  Add upload preview
  Move avatar upload to server side
  Upload working
  Improve interface to select avatar
  Init avatar selection
  • Loading branch information
engelgabriel committed Jun 1, 2015
2 parents da5e3d5 + 6f7c002 commit a2135f6
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ percolate:migrations
underscorestring:underscore.string
meteorhacks:kadira
yasaricli:slugify
jparker:gravatar
http
cfs:filesystem
cfs:standard-packages
4 changes: 4 additions & 0 deletions client/routes/router.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Router.onBeforeAction ->
this.layout('usernameLayout')
return this.render('usernamePrompt')

if Meteor.userId()? and not Meteor.user().avatarOrigin?
this.layout('usernameLayout')
return this.render('avatarPrompt')

this.next()
, {
except: ['login']
Expand Down
41 changes: 41 additions & 0 deletions client/stylesheets/base.less
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,47 @@ a.github-fork {
}
}

.avatar-suggestion-item {
height: 80px;
margin: 10px 0px;
text-align: left;

> div {
height: 80px;
width: 80px;
background-size: cover;
display: inline-block;
border: 1px solid #DDD;
font-size: 80px;
text-align: center;
line-height: 80px;
background-color: #EEE;
color: #CCC;
&.question-mark:before {
content: "?";
position: absolute;
left: 0;
width: 80px;
}
}
button {
display: inline-block;
top: -35px;
}
input[type=file] {
position: absolute !important;
width: 100%;
top: 0;
left: 0;
height: 100%;
opacity: 0;
z-index: 10000;
* {
cursor: pointer;
}
}
}

@media all and(max-width: 1100px) {
#rocket-chat {
.flex-opened {
Expand Down
53 changes: 53 additions & 0 deletions client/views/avatar/avatar.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Template.avatarPrompt.onCreated ->
self = this
self.suggestions = new ReactiveVar
self.upload = new ReactiveVar

self.getSuggestions = ->
self.suggestions.set undefined
Meteor.call 'getAvatarSuggestion', (error, avatars) ->
self.suggestions.set
ready: true
avatars: avatars

self.getSuggestions()


Template.avatarPrompt.helpers
suggestions: ->
return Template.instance().suggestions.get()

upload: ->
return Template.instance().upload.get()


Template.avatarPrompt.events
'click .select-service': (e) ->
Meteor.call 'setAvatarFromService', this.blob, this.service, ->
console.log arguments

'click .login-with-service': (event, template) ->
loginWithService = "loginWith#{_.capitalize(this)}"

serviceConfig = {}

Meteor[loginWithService] serviceConfig, (error) ->
if error?.error is 'github-no-public-email'
alert t("loginServices.github_no_public_email")
return

console.log error
if error?
toastr.error error.message
return

template.getSuggestions()

'change .myFileInput': (event, template) ->
FS.Utility.eachFile event, (blob) ->
reader = new FileReader()
reader.readAsDataURL(blob)
reader.onloadend = ->
template.upload.set
service: 'upload'
blob: reader.result
74 changes: 74 additions & 0 deletions client/views/avatar/avatar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template name="avatarSuggestion">
{{#if .}}
<div class="avatar-suggestion-item">
<div style="background-image: url({{blob}});">
</div>
<button type="button" class="button primary select-service">{{_ "avatar.Use_service_avatar" service}}</button>
</div>
{{/if}}
</template>

<template name="avatarSuggestionLogin">
{{#if .}}
<div class="avatar-suggestion-item">
<div class="question-mark"></div>
<button type="button" class="button primary login-with-service">{{_ "avatar.Login_with" .}}</button>
</div>
{{/if}}
</template>

<template name="avatarPrompt">
<form id="login-card" method='/'>
<div class="fields">
<h2>{{_ "avatar.Select_an_avatar"}}</h2>
</div>
<div class="fields">
<div class='input-text active'>
<div class='field'>
<div class="avatar-suggestions">
{{#if suggestions.ready}}
{{> avatarSuggestion suggestions.avatars.gravatar}}
{{> avatarSuggestion suggestions.avatars.facebook}}
{{> avatarSuggestion suggestions.avatars.google}}
{{> avatarSuggestion suggestions.avatars.github}}

{{#unless suggestions.avatars.gravatar}}
{{> avatarSuggestionLogin 'gravatar'}}
{{/unless}}
{{#unless suggestions.avatars.facebook}}
{{> avatarSuggestionLogin 'facebook'}}
{{/unless}}
{{#unless suggestions.avatars.google}}
{{> avatarSuggestionLogin 'google'}}
{{/unless}}
{{#unless suggestions.avatars.github}}
{{> avatarSuggestionLogin 'github'}}
{{/unless}}

<div class="avatar-suggestion-item">
<div style="background-image: url({{upload.blob}});" class="{{#unless upload}}question-mark{{/unless}}">
</div>
{{#with upload}}
<button type="button" class="button primary select-service">{{_ "avatar.Use_uploaded_avatar"}}</button>
{{/with}}
{{#unless upload}}
<button type="button" class="button primary">{{_ "avatar.Select_file"}}
<input type="file" class="myFileInput">
</button>
{{/unless}}
</div>
{{else}}
{{_ "usernameRegistration.Loading_suggestion"}}
{{/if}}
</div>
</div>
</div>
</div>

{{#if username.ready}}
<div class="submit">
<button data-loading-text="{{_ "general.Please_wait"}}..." class='button primary login'><span>{{_ "usernameRegistration.Use_this_username"}}</span></button>
</div>
{{/if}}
</form>
</template>
7 changes: 7 additions & 0 deletions i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,12 @@
},
"error": {
"Not_found_or_not_allowed": "Not Found or Not Allowed"
},
"avatar": {
"Use_service_avatar": "Use %s avatar",
"Login_with": "Login with %s",
"Select_an_avatar": "Select an avatar",
"Use_uploaded_avatar": "Use uploaded avatar",
"Select_file": "Select file"
}
}
7 changes: 7 additions & 0 deletions i18n/pt.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,12 @@
},
"error": {
"Not_found_or_not_allowed": "Não encontrado ou não permitido"
},
"avatar": {
"Use_service_avatar": "Use o avatar de %s",
"Login_with": "Login com %s",
"Select_an_avatar": "Selecione um avatar",
"Use_uploaded_avatar": "Use o avatar de upload",
"Select_file": "Selecione um arquivo"
}
}
40 changes: 40 additions & 0 deletions lib/file.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
uploadPath = "~/uploads"

store = new FS.Store.FileSystem "avatars",
path: uploadPath
fileKeyMaker: (fileObj) ->
filename = fileObj.name()
filenameInStore = fileObj.name({store: 'avatars'})

return filenameInStore || filename

@Avatars = new FS.Collection "avatars",
stores: [store]

@Avatars.allow
insert: ->
return true
update: ->
return true
download: ->
return true

Meteor.startup ->
if Meteor.isServer
FS.HTTP.mount ['/avatar/:filename'], ->
self = this
opts = FS.Utility.extend({}, self.query || {}, self.params || {})

collectionName = opts.collectionName

collection = FS._collections['avatars']

file = if collection? then collection.findOne({ "copies.avatars.key": opts.filename }) else null

return {
collection: collection
file: file
storeName: 'avatars'
download: opts.download
filename: opts.filename
}
43 changes: 43 additions & 0 deletions server/methods/getAvatarSuggestion.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Meteor.methods
getAvatarSuggestion: ->
if not Meteor.userId()
throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

user = Meteor.user()

avatars = []

if user.services.facebook?.id?
avatars.push
service: 'facebook'
url: "https://graph.facebook.com/#{user.services.facebook.id}/picture?type=large"

if user.services.google?.picture?
avatars.push
service: 'google'
url: user.services.google.picture

if user.services.github?.username?
avatars.push
service: 'github'
url: "https://avatars.githubusercontent.com/#{user.services.github.username}?s=200"

if user.emails?.length > 0
for email in user.emails when email.verified is true
avatars.push
service: 'gravatar'
url: Gravatar.imageUrl email.address

validAvatars = {}
for avatar in avatars
try
result = HTTP.get avatar.url, npmRequestOptions: {encoding: null}
if result.statusCode is 200
blob = "data:#{result.headers['content-type']};base64,"
blob += Buffer(result.content, 'binary').toString('base64')
avatar.blob = blob
validAvatars[avatar.service] = avatar
catch e
# ...

return validAvatars
14 changes: 14 additions & 0 deletions server/methods/setAvatarFromService.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Meteor.methods
setAvatarFromService: (image, service) ->
if not Meteor.userId()
throw new Meteor.Error 203, '[methods] typingStatus -> Usuário não logado'

user = Meteor.user()

file = new FS.File image
file.attachData image, ->
file.name user.username

Avatars.insert file, (err, fileObj) ->
Meteor.users.update {_id: user._id}, {$set: {avatarOrigin: service}}

1 change: 1 addition & 0 deletions server/publications.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Meteor.publish 'userData', ->
status: 1
statusDefault: 1
statusConnection: 1
avatarOrigin: 1
emails: 1
'services.facebook.id': 1
'services.google.picture': 1
Expand Down

0 comments on commit a2135f6

Please sign in to comment.