2014-05-27 15:33:50 +02:00
|
|
|
# Homework - Client Side
|
2014-06-05 17:03:10 +02:00
|
|
|
# Utilities
|
2014-09-29 10:14:30 +02:00
|
|
|
tick = new Deps.Dependency()
|
|
|
|
Meteor.setInterval (-> tick.changed();), 15000
|
|
|
|
|
2014-05-27 15:33:50 +02:00
|
|
|
notes = new Meteor.Collection "notes"
|
2014-09-29 10:14:30 +02:00
|
|
|
userSub = Meteor.subscribe 'user'
|
2014-05-28 18:45:41 +02:00
|
|
|
getUser = -> Meteor.user()
|
2014-05-31 18:09:56 +02:00
|
|
|
deleteAccount = ->
|
|
|
|
Meteor.call 'deleteMe', (r) -> if r is yes then Router.go 'home'
|
2014-05-28 18:45:41 +02:00
|
|
|
amIValid = ->
|
|
|
|
return no unless getUser()
|
|
|
|
return yes for mail in getUser().emails when mail.verified is yes; no
|
2014-05-30 08:27:49 +02:00
|
|
|
|
2014-05-31 18:09:56 +02:00
|
|
|
# Common Helpers for the Templates
|
2014-09-29 10:14:30 +02:00
|
|
|
UI.registerHelper "version", -> "1.1.1"
|
|
|
|
UI.registerHelper "status", -> Meteor.status()
|
|
|
|
UI.registerHelper "loading", -> Meteor.loggingIn() or !Meteor.status().connected
|
2014-05-31 19:31:56 +02:00
|
|
|
UI.registerHelper "email", ->
|
|
|
|
if getUser() then return getUser().emails[0].address
|
2014-05-28 18:45:41 +02:00
|
|
|
UI.registerHelper "verified", -> amIValid()
|
2014-05-27 15:33:50 +02:00
|
|
|
|
2014-05-30 10:33:32 +02:00
|
|
|
# Router
|
2014-05-31 18:09:56 +02:00
|
|
|
###
|
2014-06-01 11:47:27 +02:00
|
|
|
Important: 'home' dispatches the user to the correct landing page.
|
2014-05-31 18:09:56 +02:00
|
|
|
Routes are client side, but even if by hacking the client you can access pages
|
|
|
|
without being logged in, it's impossible to inteact with data because
|
2014-06-01 11:47:27 +02:00
|
|
|
the server doesn't let the user if he doesn't have permission. It's still safe.
|
2014-05-31 18:09:56 +02:00
|
|
|
###
|
2014-05-30 10:33:32 +02:00
|
|
|
Router.configure
|
|
|
|
layoutTemplate: 'layout'
|
|
|
|
loadingTemplate: 'loading'
|
2014-05-30 15:20:27 +02:00
|
|
|
notFoundTemplate: '404'
|
2014-09-29 10:14:30 +02:00
|
|
|
action: ->
|
|
|
|
if Meteor.status().connected is no
|
|
|
|
@render 'reconnect'
|
|
|
|
else if Meteor.loggingIn()
|
|
|
|
@render 'loading'
|
|
|
|
else @render()
|
2014-09-24 10:59:52 +02:00
|
|
|
|
|
|
|
loggedInController = RouteController.extend
|
2014-09-29 10:14:30 +02:00
|
|
|
action: ->
|
|
|
|
if Meteor.status().connected is no
|
|
|
|
@render 'reconnect'
|
|
|
|
else if !@ready() or Meteor.loggingIn()
|
|
|
|
@render 'loading'
|
|
|
|
else @render()
|
2014-09-24 10:59:52 +02:00
|
|
|
onBeforeAction: ->
|
|
|
|
if not getUser() then Router.go 'home'
|
|
|
|
if not amIValid() then Router.go 'verifyEmail'
|
|
|
|
guestController = RouteController.extend
|
2014-09-29 10:14:30 +02:00
|
|
|
action: ->
|
|
|
|
if Meteor.status().connected is no
|
|
|
|
@render 'reconnect'
|
|
|
|
else @render()
|
2014-09-24 10:59:52 +02:00
|
|
|
onBeforeAction: ->
|
|
|
|
if getUser()
|
|
|
|
if not amIValid() then Router.go 'verifyEmail' else Router.go 'home'
|
|
|
|
|
2014-05-30 10:33:32 +02:00
|
|
|
Router.map ->
|
|
|
|
@route 'home',
|
2014-05-31 19:31:56 +02:00
|
|
|
path: '/'
|
2014-06-05 17:03:10 +02:00
|
|
|
template: 'homepage'
|
2014-06-01 15:42:02 +02:00
|
|
|
action: -> @render 'homepage', to: 'outside'
|
2014-05-31 18:09:56 +02:00
|
|
|
onBeforeAction: ->
|
|
|
|
# Dispatch user to the right landing page based on his account status
|
2014-05-30 15:20:27 +02:00
|
|
|
if getUser()
|
|
|
|
if amIValid() is yes then Router.go 'notes' else Router.go 'verifyEmail'
|
2014-09-24 10:59:52 +02:00
|
|
|
@route 'login', controller: guestController
|
|
|
|
@route 'register', controller: guestController
|
|
|
|
@route 'account', controller: loggedInController
|
2014-05-30 10:33:32 +02:00
|
|
|
@route 'notes',
|
2014-06-03 15:40:59 +02:00
|
|
|
path: '/notes/:_id?'
|
2014-09-24 10:59:52 +02:00
|
|
|
waitOn: -> Meteor.subscribe 'my-notes'
|
2014-06-03 15:40:59 +02:00
|
|
|
data: -> notes.findOne _id: @params._id
|
2014-09-24 10:59:52 +02:00
|
|
|
controller: loggedInController
|
2014-06-03 15:40:59 +02:00
|
|
|
@route 'archive',
|
|
|
|
path: '/archive/:_id?'
|
2014-09-24 10:59:52 +02:00
|
|
|
waitOn: -> @notes = Meteor.subscribe 'archive'
|
|
|
|
onStop: -> @notes.stop()
|
|
|
|
controller: loggedInController
|
2014-05-30 10:33:32 +02:00
|
|
|
@route 'verifyEmail',
|
2014-05-30 11:40:15 +02:00
|
|
|
path: '/verify/:token?'
|
2014-05-30 10:33:32 +02:00
|
|
|
template: 'verifyEmail'
|
2014-09-29 10:14:30 +02:00
|
|
|
controller: guestController
|
2014-05-30 10:33:32 +02:00
|
|
|
onBeforeAction: ->
|
2014-09-24 10:59:52 +02:00
|
|
|
# Automatic verification
|
2014-05-31 19:31:56 +02:00
|
|
|
if @params.token? and @params.token isnt ""
|
2014-09-24 10:59:52 +02:00
|
|
|
@render 'loading'
|
2014-05-30 11:40:15 +02:00
|
|
|
Accounts.verifyEmail @params.token, (err) ->
|
2014-05-31 19:31:56 +02:00
|
|
|
if err
|
2014-09-24 10:59:52 +02:00
|
|
|
errCallback err; Router.go 'verifyEmail', token: @params.token
|
2014-05-31 19:31:56 +02:00
|
|
|
else Router.go 'home'
|
2014-06-01 15:42:02 +02:00
|
|
|
@route 'homepage', action: -> @render '404'
|
2014-05-30 15:20:27 +02:00
|
|
|
@route '404', path: '*'
|
2014-05-30 11:40:15 +02:00
|
|
|
|
2014-05-31 19:31:56 +02:00
|
|
|
# You can't set a callback for when the user logs in using a cookie so...
|
|
|
|
# Cheap ass work around for routing the user after he logs in with a token
|
|
|
|
Deps.autorun ->
|
2014-06-03 14:34:06 +02:00
|
|
|
t = Router.current(); return unless getUser() and t and t.lookupTemplate
|
|
|
|
temp = t.lookupTemplate()
|
|
|
|
if temp is 'login' or temp is 'homepage' or temp is 'try'
|
|
|
|
Router.go 'home'
|
2014-05-31 19:31:56 +02:00
|
|
|
|
2014-05-31 18:09:56 +02:00
|
|
|
# Client Templates
|
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# Some utility callbacks
|
2014-06-01 11:47:27 +02:00
|
|
|
logoutCallback = (err) ->
|
|
|
|
if err then errCallback err
|
|
|
|
else Router.go 'home'; Meteor.unsubscribe "my-notes"
|
2014-05-31 18:09:56 +02:00
|
|
|
errCallback = (err) ->
|
|
|
|
if err.reason
|
|
|
|
showError msg: err.reason
|
|
|
|
else showErrror msg: err
|
2014-05-30 10:33:32 +02:00
|
|
|
|
2014-09-29 10:14:30 +02:00
|
|
|
Template.reconnect.time = ->
|
|
|
|
tick.depend()
|
|
|
|
if Meteor.status().retryTime
|
|
|
|
'(retrying '+moment(Meteor.status().retryTime).fromNow()+')'
|
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# 3 Buttons navigation Menu
|
2014-05-31 18:09:56 +02:00
|
|
|
Template.menu.events
|
|
|
|
'click .go-home': -> Router.go 'home'
|
|
|
|
'click .go-account': -> Router.go 'account'
|
|
|
|
'click .go-archive': -> Router.go 'archive'
|
2014-05-30 08:27:49 +02:00
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# Account Page
|
2014-09-26 12:59:23 +02:00
|
|
|
Template.account.dateformat = -> getUser().dateformat
|
2014-05-30 10:33:32 +02:00
|
|
|
Template.account.events
|
2014-09-26 12:59:23 +02:00
|
|
|
'click #reset-settings': (e,t) ->
|
|
|
|
t.find('#set-date-format').value = "MM/DD/YYYY"
|
|
|
|
'click #save-settings': (e,t) ->
|
|
|
|
Meteor.users.update getUser()._id,
|
|
|
|
$set: dateformat: t.find('#set-date-format').value
|
|
|
|
showError msg: 'Settings saved', type: 'success'
|
|
|
|
'click #btn-logout': -> Meteor.logout logoutCallback
|
2014-05-31 19:31:56 +02:00
|
|
|
'click #btn-delete-me': -> deleteAccount()
|
2014-05-28 12:50:30 +02:00
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# Notes list
|
2014-06-01 11:47:27 +02:00
|
|
|
Template.notelist.active = ->
|
|
|
|
return no unless Router.current() and Router.current().data()
|
|
|
|
return @_id is Router.current().data()._id
|
2014-06-03 15:40:59 +02:00
|
|
|
Template.notelist.empty = -> Template.notelist.notelist().length is 0
|
2014-06-01 11:47:27 +02:00
|
|
|
Template.notelist.getDate = ->
|
2014-09-26 12:59:23 +02:00
|
|
|
return unless @date
|
2014-09-29 10:14:30 +02:00
|
|
|
tick.depend()
|
2014-09-26 12:59:23 +02:00
|
|
|
#dif = moment(@date, getUser().dateformat).diff(moment(), 'days')
|
|
|
|
dif = moment.unix(@date).diff(moment(), 'days')
|
|
|
|
color = "primary"
|
|
|
|
color = "info" if dif < 7
|
|
|
|
color = "warning" if dif is 1
|
|
|
|
color = "danger" if dif < 1
|
|
|
|
msg: moment.unix(@date).fromNow(), color: color
|
2014-06-03 14:34:06 +02:00
|
|
|
Template.notelist.notelist = ->
|
2014-06-03 15:40:59 +02:00
|
|
|
notes.find({ archived: no },{ sort: date: 1}).fetch()
|
2014-05-30 10:33:32 +02:00
|
|
|
Template.notelist.events
|
2014-06-03 15:40:59 +02:00
|
|
|
'click .close-note': -> notes.update @_id, $set: archived: yes
|
2014-09-23 10:32:08 +02:00
|
|
|
'click .edit-note': -> Router.go 'notes'
|
2014-05-27 15:33:50 +02:00
|
|
|
'keypress #newNote': (e,template) ->
|
2014-05-28 10:19:06 +02:00
|
|
|
if e.keyCode is 13 and template.find('#newNote').value isnt ""
|
2014-05-28 12:50:30 +02:00
|
|
|
notes.insert
|
2014-05-27 15:33:50 +02:00
|
|
|
title: template.find('#newNote').value
|
2014-06-01 11:47:27 +02:00
|
|
|
content: "", date: no, archived: no, userId: Meteor.userId()
|
2014-05-27 15:33:50 +02:00
|
|
|
template.find('#newNote').value = ""
|
|
|
|
|
2014-06-03 15:40:59 +02:00
|
|
|
# Archive
|
|
|
|
Template.archivedlist.empty = -> Template.archivedlist.archived().length is 0
|
2014-09-26 12:59:23 +02:00
|
|
|
Template.archivedlist.getDate = Template.notelist.getDate
|
2014-06-03 15:40:59 +02:00
|
|
|
Template.archivedlist.archived = ->
|
|
|
|
notes.find({ archived: yes },{ sort: date: 1}).fetch()
|
|
|
|
Template.archivedlist.events =
|
|
|
|
'click .close-note': -> notes.remove @_id
|
|
|
|
'click .note': -> notes.update @_id, $set: archived: no
|
|
|
|
'click .clear': ->
|
|
|
|
notes.remove item._id for item in Template.archivedlist.archived()
|
|
|
|
|
2014-05-27 15:33:50 +02:00
|
|
|
# Note Editor
|
2014-06-01 11:47:27 +02:00
|
|
|
Template.editor.note = -> Router.current().data()
|
2014-09-29 09:04:21 +02:00
|
|
|
Template.editor.dateformat = -> getUser().dateformat
|
2014-09-26 12:59:23 +02:00
|
|
|
Template.editor.formattedDate = ->
|
|
|
|
return unless @date
|
|
|
|
moment.unix(@date).format(getUser().dateformat)
|
2014-05-27 15:33:50 +02:00
|
|
|
saveCurrentNote = (t,e) ->
|
2014-05-31 18:09:56 +02:00
|
|
|
if e and e.keyCode isnt 13 then return
|
2014-09-26 12:59:23 +02:00
|
|
|
dat = no
|
|
|
|
if t.find('.date').value isnt ""
|
|
|
|
dat = moment(t.find('.date').value,getUser().dateformat)
|
|
|
|
if dat.isValid()
|
|
|
|
dat = dat.unix()
|
|
|
|
else
|
|
|
|
dat = no; showError msg: 'Invalid date'
|
|
|
|
t.find('.date').value = ""
|
2014-05-30 10:33:32 +02:00
|
|
|
notes.update Router.current().data()._id,
|
2014-05-27 15:33:50 +02:00
|
|
|
$set:
|
2014-05-28 14:57:43 +02:00
|
|
|
title: t.find('.editor-title').value
|
2014-05-27 15:33:50 +02:00
|
|
|
content: t.find('.area').value
|
2014-09-26 12:59:23 +02:00
|
|
|
date: dat
|
2014-05-27 15:33:50 +02:00
|
|
|
Template.editor.events
|
2014-06-05 17:03:10 +02:00
|
|
|
'click .close-editor': -> Router.go 'notes'
|
2014-05-27 15:33:50 +02:00
|
|
|
'click .save-editor': (e,t) -> saveCurrentNote t
|
2014-09-26 12:59:23 +02:00
|
|
|
'click .set-date': (e,t) ->
|
|
|
|
t.find('.date').value = moment().add(1,'days').format(getUser().dateformat)
|
2014-05-27 15:33:50 +02:00
|
|
|
'keypress .title': (e,t) -> saveCurrentNote t, e
|
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# Notifications (not used yet)
|
2014-05-27 15:33:50 +02:00
|
|
|
alerts = []
|
|
|
|
alertDep = new Deps.Dependency
|
|
|
|
# Show a notification
|
|
|
|
notify = (data) ->
|
2014-05-28 12:50:30 +02:00
|
|
|
alerts.push
|
2014-05-27 15:33:50 +02:00
|
|
|
title: data.title
|
|
|
|
msg: data.msg
|
|
|
|
type: data.type or "danger"
|
2014-05-28 12:50:30 +02:00
|
|
|
alertDep.changed()
|
2014-05-27 15:33:50 +02:00
|
|
|
# Clear all notifications
|
|
|
|
clearNotifications = -> alerts.clear(); alertDep.changed()
|
|
|
|
# Get all the notifications
|
|
|
|
Template.notifications.notification = -> alertDep.depend(); alerts
|
2014-05-28 10:19:06 +02:00
|
|
|
Template.notifications.events
|
2014-05-27 15:33:50 +02:00
|
|
|
'click .close-notification': (e,template) ->
|
|
|
|
alerts.splice alerts.indexOf(this), 1
|
|
|
|
alertDep.changed()
|
2014-05-28 10:19:06 +02:00
|
|
|
|
2014-05-28 12:50:30 +02:00
|
|
|
# "Error" visualization template
|
|
|
|
errorDep = new Deps.Dependency; shownError = undefined
|
|
|
|
showError = (err) ->
|
|
|
|
shownError = err; shownError.type = err.type or "danger"
|
|
|
|
errorDep.changed()
|
|
|
|
clearError = -> shownError = undefined; errorDep.changed()
|
|
|
|
Template.error.error = -> errorDep.depend(); shownError
|
|
|
|
Template.error.events 'click .close': -> clearError()
|
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# Verify Email page
|
2014-09-24 10:59:52 +02:00
|
|
|
Template.verifyEmail.token = -> Router.current().params.token
|
2014-05-28 18:45:41 +02:00
|
|
|
Template.verifyEmail.events
|
|
|
|
'click #btn-verify': (e,template) ->
|
2014-05-31 19:31:56 +02:00
|
|
|
t = template.find('#token-field').value; t = t.split("/")
|
|
|
|
t = t[t.length-1] # Remove all the part before the last "/"
|
|
|
|
console.log "Email ver. using token: "+template.find('#token-field').value
|
|
|
|
Accounts.verifyEmail t, (err) ->
|
2014-05-30 10:33:32 +02:00
|
|
|
if err then errCallback err else Router.go 'notes'
|
2014-05-28 18:45:41 +02:00
|
|
|
'click #btn-resend': ->
|
2014-05-30 10:33:32 +02:00
|
|
|
Meteor.call 'resendConfirmEmail', (err) ->
|
|
|
|
if err
|
|
|
|
errCallback err
|
|
|
|
else showError { type:"success", msg: "Confirmation email sent" }
|
2014-05-31 18:09:56 +02:00
|
|
|
'click #btn-delete': -> deleteAccount()
|
2014-05-30 10:33:32 +02:00
|
|
|
'click #btn-logout': -> Meteor.logout logoutCallback
|
2014-05-28 18:45:41 +02:00
|
|
|
|
2014-05-31 18:09:56 +02:00
|
|
|
# Login
|
|
|
|
loginRequest = (e,template) ->
|
|
|
|
if e and e.keyCode isnt 13 then return
|
|
|
|
mail = template.find('#l-mail').value; pass = template.find('#l-pass').value
|
|
|
|
Meteor.loginWithPassword mail, pass, (err) ->
|
2014-05-31 19:31:56 +02:00
|
|
|
if err then errCallback err else Router.go 'notes'
|
2014-05-31 18:09:56 +02:00
|
|
|
|
|
|
|
Template.login.events
|
|
|
|
'keypress .login': (e,template) -> loginRequest e,template
|
|
|
|
'click #login-btn': (e,template) -> loginRequest null,template
|
2014-05-28 18:45:41 +02:00
|
|
|
|
2014-06-05 17:03:10 +02:00
|
|
|
# New Account page
|
2014-05-31 18:09:56 +02:00
|
|
|
registerRequest = (e,template) ->
|
|
|
|
if e and e.keyCode isnt 13 then return
|
|
|
|
mail = template.find('#r-mail').value; pass = template.find('#r-pass').value
|
|
|
|
pass2 = template.find('#r-pass-2').value
|
|
|
|
if not mail
|
|
|
|
showError msg: "Please enter an Email"
|
|
|
|
else if not pass
|
|
|
|
showError msg: "Please enter a password"
|
|
|
|
else if pass.length < 8
|
|
|
|
showError msg: "Password too short"
|
|
|
|
else if pass2 isnt pass
|
|
|
|
showError msg: "The passwords don't match"
|
|
|
|
else # Sending actual registration request
|
|
|
|
try
|
|
|
|
Accounts.createUser {
|
|
|
|
email: mail,
|
|
|
|
password: pass
|
|
|
|
}, (err) -> if err then errCallback err else Router.go 'confirmEmail'
|
|
|
|
catch err
|
|
|
|
showError msg: err
|
|
|
|
Template.register.events
|
|
|
|
'click #register-btn': (e,t) -> registerRequest null,t
|
|
|
|
'keypress .register': (e,t) -> registerRequest e,t
|