From 85408a14d3267c1233a67cde5a0d5c1b33f88044 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Tue, 1 Nov 2016 11:18:27 +0800 Subject: [PATCH 01/16] Initial notification centre using mailboxer --- .codeclimate.yml | 2 + Gemfile | 1 + Gemfile.lock | 10 ++ app/assets/images/.DS_Store | Bin 16388 -> 0 bytes app/assets/images/topright_sprite.png | Bin 1646 -> 3445 bytes app/assets/images/user_sprite.png | Bin 1936 -> 2706 bytes app/assets/stylesheets/application.scss.erb | 6 +- app/assets/stylesheets/apps.css.erb | 8 ++ app/assets/stylesheets/clean.css.erb | 14 ++- app/assets/stylesheets/mobile.scss.erb | 28 ++++- app/assets/stylesheets/notifications.scss.erb | 71 +++++++++++++ app/controllers/access_controller.rb | 16 +-- app/controllers/notifications_controller.rb | 97 ++++++++++++++++++ app/controllers/users/sessions_controller.rb | 29 ++++-- app/controllers/users_controller.rb | 7 +- app/helpers/application_helper.rb | 16 +++ app/mailers/application_mailer.rb | 4 + app/models/user.rb | 17 +++ app/views/layouts/_account.html.erb | 8 +- app/views/layouts/_mobilemenu.html.erb | 12 ++- app/views/layouts/_upperelements.html.erb | 11 ++ app/views/layouts/application.html.erb | 6 +- app/views/layouts/doorkeeper.html.erb | 1 + .../message_mailer/new_message_email.html.erb | 20 ++++ .../message_mailer/new_message_email.text.erb | 10 ++ .../reply_message_email.html.erb | 20 ++++ .../reply_message_email.text.erb | 10 ++ .../new_notification_email.html.erb | 10 ++ .../new_notification_email.text.erb | 2 + .../map_mailer/access_request_email.html.erb | 27 ++--- .../map_mailer/access_request_email.text.erb | 2 +- .../map_mailer/invite_to_edit_email.html.erb | 29 ++---- .../map_mailer/invite_to_edit_email.text.erb | 2 +- app/views/notifications/_header.html.erb | 18 ++++ app/views/notifications/index.html.erb | 33 ++++++ app/views/notifications/mark_read.js.erb | 6 ++ app/views/notifications/mark_unread.js.erb | 6 ++ app/views/notifications/show.html.erb | 15 +++ app/views/shared/_back_to_mapping.html.erb | 3 + .../shared/_mailer_unsubscribe_link.html.erb | 3 + .../shared/_mailer_unsubscribe_link.text.erb | 5 + app/views/users/edit.html.erb | 38 ++++--- config/application.rb | 11 +- config/environments/development.rb | 14 +-- config/initializers/mailboxer.rb | 21 ++++ config/routes.rb | 35 +++++-- ...31231_create_mailboxer.mailboxer_engine.rb | 65 ++++++++++++ ...dd_conversation_optout.mailboxer_engine.rb | 15 +++ ...33_add_missing_indices.mailboxer_engine.rb | 20 ++++ ..._to_mailboxer_receipts.mailboxer_engine.rb | 8 ++ ...61125175229_add_emails_allowed_to_users.rb | 5 + db/schema.rb | 63 +++++++++++- doc/metamaps-qa-steps.md | 7 ++ 53 files changed, 778 insertions(+), 109 deletions(-) delete mode 100644 app/assets/images/.DS_Store mode change 100755 => 100644 app/assets/images/user_sprite.png create mode 100644 app/assets/stylesheets/notifications.scss.erb create mode 100644 app/controllers/notifications_controller.rb create mode 100644 app/views/mailboxer/message_mailer/new_message_email.html.erb create mode 100644 app/views/mailboxer/message_mailer/new_message_email.text.erb create mode 100644 app/views/mailboxer/message_mailer/reply_message_email.html.erb create mode 100644 app/views/mailboxer/message_mailer/reply_message_email.text.erb create mode 100644 app/views/mailboxer/notification_mailer/new_notification_email.html.erb create mode 100644 app/views/mailboxer/notification_mailer/new_notification_email.text.erb create mode 100644 app/views/notifications/_header.html.erb create mode 100644 app/views/notifications/index.html.erb create mode 100644 app/views/notifications/mark_read.js.erb create mode 100644 app/views/notifications/mark_unread.js.erb create mode 100644 app/views/notifications/show.html.erb create mode 100644 app/views/shared/_back_to_mapping.html.erb create mode 100644 app/views/shared/_mailer_unsubscribe_link.html.erb create mode 100644 app/views/shared/_mailer_unsubscribe_link.text.erb create mode 100644 config/initializers/mailboxer.rb create mode 100644 db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb create mode 100644 db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb create mode 100644 db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb create mode 100644 db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb create mode 100644 db/migrate/20161125175229_add_emails_allowed_to_users.rb diff --git a/.codeclimate.yml b/.codeclimate.yml index 719b2807..a187069d 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -20,6 +20,8 @@ engines: enabled: true rubocop: enabled: true + exclude_fingerprints: + - 74f18007b920e8d81148d2f6a2756534 ratings: paths: - 'Gemfile.lock' diff --git a/Gemfile b/Gemfile index 9fd59b62..0d8e8d7a 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem 'exception_notification' gem 'httparty' gem 'json' gem 'kaminari' +gem 'mailboxer' gem 'paperclip' gem 'pg' gem 'pundit' diff --git a/Gemfile.lock b/Gemfile.lock index 92215068..d104cb51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,6 +65,12 @@ GEM brakeman (3.4.0) builder (3.2.2) byebug (9.0.5) + carrierwave (0.11.2) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + json (>= 1.7) + mime-types (>= 1.16) + mimemagic (>= 0.3.0) climate_control (0.0.3) activesupport (>= 3.0) cocaine (0.5.8) @@ -125,6 +131,9 @@ GEM nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) + mailboxer (0.14.0) + carrierwave (>= 0.5.8) + rails (>= 4.2.0) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) @@ -284,6 +293,7 @@ DEPENDENCIES json json-schema kaminari + mailboxer paperclip pg pry-byebug diff --git a/app/assets/images/.DS_Store b/app/assets/images/.DS_Store deleted file mode 100644 index 2bbe5f6a30a17587916373a8968facc58339b0db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16388 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 diff --git a/app/assets/images/topright_sprite.png b/app/assets/images/topright_sprite.png index 163dd6f7330b3323bf0e6d498a0c270a28cafc5c..4c969887dd42f30ac81eab16de5969d56c4dd85e 100644 GIT binary patch literal 3445 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 X&6~pKtb@=s00000NkvXXu0mjfF#c`> literal 1646 zcmV-!29f!RP)B&0pP<309a}OunIn42_+1B$_$qpbd%>w2k)i-B~J#( z`n%+SfacYW_m`B{0l+aJb^#z48+?-fuL8h7(tl@Z`WAp~iU@_E-{0T&lFbg_^6y;; z5R^U=%BfgB9!q7$chdiaz@y*Z-hPqC{*(SIhyN7-NVOnTL%&ahPe8Db0aOVM@N-?) zEd>C&d~YTFr(Cpex<-t{xqPonX%Z?5_^#3wFD_aga&?}S@9Nisk1pR^PYnQA{Xg0! zTfYB?(g&%l=#bN)ng|7eCY*YpQ(9$&IxvF=0K^ag?(XiIh;&lrpnbHDK9em!_nY(| z5*c8hkpcEG3}8~vSY<>6zju|e7%-rF69AMJ5R>J$o12@@zLPFL{!6L?&H(~AM+o{M zXKTJ606{zCv>6q%R{>OSp#Kd5L%wcLqQun#UZf040YPZ+Efm`8Io&46Pz0(jWgU6l zS`Ri{Qyyy(2mlH^zbY9!_&hj#u6W5@8UQIE$Ps)B0=lX(xR!Z&@fCfJrWUOJ)&vd+ zKB*2i11Y0y-((B`#DE}Y@aa7m(m`8r%(YASseZwBNHh)sXm$Vy0YQo26Jfg2#a*du zVKv|qeYUCrQ^;8Wzy|~+gHP!_L=P|kMgqYY!6)PHL+krc1L{`*SUPkt=pdAr}wXO%~)^jEScnqwqRD`|&3PB41#MuErZE8#gfFV7AAW)k@JDIGB*?6SfDlRf~HeF7E#P;l{cXSwQ65dew_ zHXdzYuWq1iBZyFaJIZe&a&N}u6pE(=0Bj%o?guyku(64`QGX1>0EntiI@?a5t1awk z>a=5{ngI~mHr6zNw-|IVz?uOt0{~zZ5V;E|EZe{r|341js9i%d=wy^Hl!ytx2Q*ay zOO84l8yg!N8ylN6Aw+$3b+wbemw)r~0WQt1udiF_2Xvm1%VU5BgAXh~jxXi0PQGrW zS3FNb=yNpJLA3H^O^#KH1t8dObuy#{1O|YB0q_cd*|H?$dP?;$Alv5w=?sAJ0Ptai z0W38DG>*@&0R+-}$_$qpbd%>w2gm0FC}b6VJ^;|Xy7B&!^14u#8vwc3;P`w1buxZF zz_)2=n<7Gixu}*=rS2-=`<3|L8k#aC|<1Be!IEK0wCcTzYf;9uNfj=d|zW0RQ5Q z@57_*yFCj4_<*2f@F~5A=m7@6NFW#^_(bIOr6(C%xHb`K&;q93!TIfony9#Ft~2Cb zLz%Xr_m^RDwYG&ts{vE%xO9q|8USKS09f?E2FpN|8breRHzok69>Gp;E)Aq6u{cLN z1prW**2_aJMRXw=L2m-#?;5ng8#{uz+rA8dfR*Y|nKY5(nx++;dow1dP&_37(0uK) z9}o+s(Zw7CAgXcG*~&3jTbQ(v50j6N0g%)-?qkH5g8|kIfEfUQQ~@!&fI~fwe!=Ff s!^H{R-zxF9BNiYcXrh^PTzo~Ef^P(DV) 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 delta 1921 zcmV-{2Y&dH6_5{*B!3BTNLh0L01FcU01FcV0GgZ_0000PbVXQnQ*UN;cVTj606}DL zVr3vnZDD6+Qe|Oed2z{QJOBU(9Z5t%RCwC#T|I6iHxwQVDpEzdfpr7fC$Iz`z}_;| zj!&?1<1UGebgl$!`p73(r49^_Zy?=3?zRX52}l{hdC&f&P=7Rs!+#+wK7f!%qv1z> zeDCp*L()etFE7O*ORG3=$pmBq4qV!GH*Rijy7W;|n%~{sm5Tc(^f7I?&(DwO{?xW$ z=jZ2J{D+5!QgMHauetwPL#cFZoFw?!1T^>Nl!kjOTN3f`1Fr#dXR^aR(pQ$w!0#3n!PIpa%h)h^j7%n!))@9q%_u68xk5ax|MjCLj}# z3CILw0x|)IDm!BV8s+f_i>H%jsRuYLG|JoJ`wVbw5U%tbpGf=}Sl zRHq@IXQ*oh|FH__6Rh!V7E&~h24NChhzpCd^DuTC!c<@qPWUFKV$uzku*n}N-9q&i z4bSlEtuL56^c81(%pIC{gEhkH^eC~rLSeq6^p(>0Ad|bWEl4tD&iFc?U_2v)cY@Qxej@UT2p9zL1X5gX$O#w<>w=@Vrff* zzCdt{R`{|n@LC^$5#m@NWQsw|XTeE*m4cdJ1Ym?X76`G%4>lo!1=b&7Z+{8>@}E*r z6KfRdgdcK7h;8#8>bOzKNwK?LXA*HL!3jUgt1;3B&h(zQ($L?mqCao06HThT;(zm2 z8v1*Sui9GEj!^lo!r86SLMbZWRXDp^{NMzjuNT3Wp<)0-$Ed^)76^T7lHUtt0x|)a zfJ{IpAQO-Y$OL2pG69)@gOs}S19(!g9~kSIhQvL?F?|$#8WQ(RHJ~4X>|@Lunn)CU z%o`dJa9V`j2gG@uTC60Ag0EUhQh!GPGc^cQh&A%E*-V)Xzm9<4inlp5wwZ+o_yT#1 zkf*66!`ICTZdPeAdzFJ-F7T{s#g~aB!EYH9Vv3AxHbCEdRz4ZN$*k}NT3{fxj1GL{ z*lNk~O#`7mGUSY}HNU$JT>0quEz3g8k@=P4{uR_UbmgPs+lIo&0N5OJwSPvs!gupX zm5+w+G8X8d5d0R1x6O8imgyAnWs6UN4R4$E!f$&N;*mnz%_@!5TWDYLDYW&*_kB0G z&%MJz@bi3tCVmbr>qx)#$}_(m&L)rv$OL2pG69)@Oh6_e6OakW1RSJz%?Aj3i_4fh z%IiefTU@&CDEHYl)+=_;_J16vJbzemw&>91{9zvx=oM+^0Hfl!4O)7$7nfyea*_m| z;rV&~t=D#XtpDHQo17$pXSVpdt+OT?z&7@XR2r6>ZIgFt@C(Tbgt&?~J_xZVfC|nm zUN{t5JnphzI3B4-1&@y3YEa{`^tGvz;ev~Ea2K{`382-2;n$rdpnubVQgSl4rd}R? zqEzH(Bk%A{y*&Jc5q=W^8(}uBw!9%Oc&brLQv#gv5f?P#>j)6vvAKG3(e^qPi=^J+ zJN}269ekTbQnI)nQzLg-uDwAt{FWxlYbwC10n9hbOM>6hI5ANMir;Xdeis~MFai|8 z&Oq^-9emfLwseu;T7U4uCpAPhIkroWR%*njN_*q$d_X1j<2}p4QdcU#v!#|)Qa|3a zG^2O;DlO3wFw=Nc(_rM-^1SAYQknQ3`jEfnNX00000NkvXX Hu0mjfD); + background-image: url(<%= asset_path('user_sprite.png') %>); } .accountSettings .accountIcon { background-position: 0 0; @@ -3076,3 +3076,7 @@ script.data-gratipay-username { display: inline; float: left; } + +.inline { + display: inline-block; +} diff --git a/app/assets/stylesheets/apps.css.erb b/app/assets/stylesheets/apps.css.erb index e6d75dd7..a88199af 100644 --- a/app/assets/stylesheets/apps.css.erb +++ b/app/assets/stylesheets/apps.css.erb @@ -129,3 +129,11 @@ box-sizing: border-box; border-radius: 2px; } + +.back-to-mapping { + margin: 1em; + width: auto; + max-width: 100%; + box-sizing: border-box; +} + diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index e4da394b..b25816f0 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -210,7 +210,10 @@ } .addMap { background-position: -96px 0; - margin-right:10px; +} +.notificationsIcon { + background-position: -128px 0; + margin-right: 10px; // make it look more natural next to the account menu icon } .importDialog:hover { background-position: 0 -32px; @@ -758,7 +761,7 @@ } .exploreMapsCenter .authedApps .exploreMapsIcon { - background-image: url(<%= asset_data_uri('user_sprite.png') %>); + background-image: url(<%= asset_path('user_sprite.png') %>); background-position: 0 -32px; } .exploreMapsCenter .myMaps .exploreMapsIcon { @@ -781,6 +784,10 @@ background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -96px 0; } +.exploreMapsCenter .notificationsLink .exploreMapsIcon { + background-image: url(<%= asset_path 'user_sprite.png' %>); + background-position: 0 -128px; +} .authedApps:hover .exploreMapsIcon, .authedApps.active .exploreMapsIcon { background-position-x: -32px; } @@ -799,6 +806,9 @@ .sharedMaps:hover .exploreMapsIcon, .sharedMaps.active .exploreMapsIcon { background-position: -128px -32px; } +.notificationsLink:hover .exploreMapsIcon, .notificationsLink.active .exploreMapsIcon { + background-position-x: -32px; +} .mapsWrapper { /*overflow-y: auto; */ diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 01fa5f61..e7eb9a7d 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -213,8 +213,17 @@ line-height: 50px; } +#mobile_header #menu_icon .unread-notifications-dot { + top: 5px; + left: 29px; + width: 12px; + height: 12px; + border: 3px solid #eee; + border-radius: 9px; +} + #mobile_menu { - display: none; + display: none; background: #EEE; position: fixed; top: 50px; @@ -222,11 +231,20 @@ padding: 10px; width: 200px; box-shadow: 3px 3px 3px rgba(0,0,0,0.23), 3px 3px 3px rgba(0,0,0,0.16); -} -#mobile_menu li { - padding: 10px; - list-style: none; + li { + padding: 10px; + list-style: none; + + &.notifications { + position: relative; + + .unread-notifications-dot { + top: 17px; + left: 0px; + } + } + } } /* diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb new file mode 100644 index 00000000..b25bf4e8 --- /dev/null +++ b/app/assets/stylesheets/notifications.scss.erb @@ -0,0 +1,71 @@ +$unread_notifications_dot_size: 8px; +.unread-notifications-dot { + width: $unread_notifications_dot_size; + height: $unread_notifications_dot_size; + background-color: #e22; + border-radius: $unread_notifications_dot_size / 2; + position: absolute; + top: 0; + right: 0; +} + +.upperRightUI { + .notificationsIcon { + position: relative; + } +} + +.controller-notifications { + ul.notifications { + list-style: none; + } + + $menu_bar_height: 6em; + .notificationPage, + .notificationsPage { + width: auto; + max-width: 100%; + box-sizing: border-box; + margin: 1em; + margin-top: 1em + $menu_bar_height; + + & > .title { + border-bottom: 1px solid #eee; + padding-bottom: 0.25em; + margin-bottom: 0.5em; + } + + .back { + margin-top: 1em; + } + } + + + .notification { + .notification-subject { + width: 25%; + } + .notification-body { + width: 50%; + } + .notification-read-unread { + width: 10%; + } + + .notification-body, + .notification-subject, + .notification-read-unread { + display: inline-block; + vertical-align: top; + font-weight: 300; + } + + &.unread { + .notification-body, + .notification-subject, + .notification-read-unread { + font-weight: bold; + } + } + } +} diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index c48ac418..a0271981 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -21,7 +21,8 @@ class AccessController < ApplicationController def access_request request = AccessRequest.create(user: current_user, map: @map) # what about push notification to map owner? - MapMailer.access_request_email(request, @map).deliver_later + mail = MapMailer.access_request_email(request, @map) + @map.user.notify(mail.subject, mail.body) respond_to do |format| format.json do @@ -37,7 +38,9 @@ class AccessController < ApplicationController @map.add_new_collaborators(user_ids).each do |user_id| # add_new_collaborators returns array of added users, # who we then send an email to - MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)).deliver_later + user = User.find(user_id) + mail = MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)) + user.notify(mail.subject, mail.body) end @map.remove_old_collaborators(user_ids) @@ -51,7 +54,7 @@ class AccessController < ApplicationController # GET maps/:id/approve_access/:request_id def approve_access request = AccessRequest.find(params[:request_id]) - request.approve() + request.approve respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was approved' } end @@ -60,7 +63,7 @@ class AccessController < ApplicationController # GET maps/:id/deny_access/:request_id def deny_access request = AccessRequest.find(params[:request_id]) - request.deny() + request.deny respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was turned down' } end @@ -69,7 +72,7 @@ class AccessController < ApplicationController # POST maps/:id/approve_access/:request_id def approve_access_post request = AccessRequest.find(params[:request_id]) - request.approve() + request.approve respond_to do |format| format.json do head :ok @@ -80,7 +83,7 @@ class AccessController < ApplicationController # POST maps/:id/deny_access/:request_id def deny_access_post request = AccessRequest.find(params[:request_id]) - request.deny() + request.deny respond_to do |format| format.json do head :ok @@ -94,5 +97,4 @@ class AccessController < ApplicationController @map = Map.find(params[:id]) authorize @map end - end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb new file mode 100644 index 00000000..4759ef20 --- /dev/null +++ b/app/controllers/notifications_controller.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true +class NotificationsController < ApplicationController + before_action :set_receipts, only: [:index, :show, :mark_read, :mark_unread] + before_action :set_notification, only: [:show, :mark_read, :mark_unread] + before_action :set_receipt, only: [:show, :mark_read, :mark_unread] + + def index + @notifications = current_user.mailbox.notifications + + respond_to do |format| + format.html + format.json do + render json: @notifications.map do |notification| + receipt = @receipts.find_by(notification_id: notification.id) + notification.as_json.merge(is_read: receipt.is_read) + end + end + end + end + + def show + @receipt.update(is_read: true) + respond_to do |format| + format.html + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def mark_read + @receipt.update(is_read: true) + respond_to do |format| + format.js + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def mark_unread + @receipt.update(is_read: false) + respond_to do |format| + format.js + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def unsubscribe + unsubscribe_redirect_if_logged_out! + check_if_already_unsubscribed! + return if performed? # if one of these checks already redirected, we're done + + if current_user.update(emails_allowed: false) + redirect_to edit_user_path(current_user), + notice: 'You will no longer receive emails from Metamaps.' + else + flash[:alert] = 'Sorry, something went wrong. You have not been unsubscribed from emails.' + redirect_to edit_user_path(current_user) + end + end + + private + + def unsubscribe_redirect_if_logged_out! + return if current_user.present? + + flash[:notice] = 'Continue to unsubscribe from emails by logging in.' + redirect_to "#{sign_in_path}?redirect_to=#{unsubscribe_notifications_path}" + end + + def check_if_already_unsubscribed! + return if current_user.emails_allowed + + redirect_to edit_user_path(current_user), notice: 'You were already unsubscribed from emails.' + end + + def set_receipts + @receipts = current_user.mailboxer_notification_receipts + end + + def set_notification + @notification = current_user.mailbox.notifications.find_by(id: params[:id]) + end + + def set_receipt + @receipt = @receipts.find_by(notification_id: params[:id]) + end +end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index fed670ae..1c9c0a1e 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,14 +1,25 @@ -class Users::SessionsController < Devise::SessionsController - protected +# frozen_string_literal: true +module Users + class SessionsController < Devise::SessionsController + after_action :store_location, only: [:new] - def after_sign_in_path_for(resource) - stored = stored_location_for(User) - return stored if stored + protected - if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url) - super - else - request.referer || root_path + def after_sign_in_path_for(resource) + stored = stored_location_for(User) + return stored if stored + + if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url) + super + else + request.referer || root_path + end + end + + private + + def store_location + store_location_for(User, params[:redirect_to]) if params[:redirect_to] end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index a9fff9de..6b771d6f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -13,13 +13,12 @@ class UsersController < ApplicationController # GET /users/:id/edit def edit - @user = current_user - respond_with(@user) + @user = User.find(current_user.id) end # PUT /users/:id def update - @user = current_user + @user = User.find(current_user.id) if user_params[:password] == '' && user_params[:password_confirmation] == '' # not trying to change the password @@ -96,6 +95,6 @@ class UsersController < ApplicationController private def user_params - params.require(:user).permit(:name, :email, :image, :password, :password_confirmation) + params.require(:user).permit(:name, :email, :image, :password, :password_confirmation, :emails_allowed) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3221aa34..cc121cbe 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -37,4 +37,20 @@ module ApplicationHelper def invite_link "#{request.base_url}/join" + (current_user ? "?code=#{current_user.code}" : '') end + + def user_has_unread_notifications? + return @user_has_unread_notifications unless @user_has_unread_notifications.nil? + return (@user_has_unread_notifications = false) if current_user.nil? + current_user.mailboxer_notification_receipts.each do |receipt| + return (@user_has_unread_notifications = true) if receipt.is_read == false + end + @user_has_unread_notifications = false + end + + def user_unread_notification_count + return 0 if current_user.nil? + current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| + receipt.is_read ? total : total + 1 + end + end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 59a2175a..10961836 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -2,4 +2,8 @@ class ApplicationMailer < ActionMailer::Base default from: 'team@metamaps.cc' layout 'mailer' + + def deliver + raise NotImplementedError('Please use Mailboxer to send your emails.') + end end diff --git a/app/models/user.rb b/app/models/user.rb index 23ef6440..f6fcb60e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,8 @@ require 'open-uri' class User < ApplicationRecord + acts_as_messageable # mailboxer notifications + has_many :topics has_many :synapses has_many :maps @@ -108,4 +110,19 @@ class User < ApplicationRecord def settings=(val) self[:settings] = val end + + # Mailboxer hooks and helper functions + + def mailboxer_email(_message) + return email if emails_allowed + # else return nil, which sends no email + end + + def mailboxer_notifications + mailbox.notifications + end + + def mailboxer_notification_receipts + mailbox.receipts.includes(:notification).where(mailbox_type: nil) + end end diff --git a/app/views/layouts/_account.html.erb b/app/views/layouts/_account.html.erb index 748e5f1b..3d66f687 100644 --- a/app/views/layouts/_account.html.erb +++ b/app/views/layouts/_account.html.erb @@ -18,14 +18,14 @@ <%= link_to "Admin", metacodes_path %> <% end %> -
  • -
    - Share Invite -
  • <%= link_to "Apps", oauth_authorized_applications_path %>
  • +
  • +
    + Share Invite +
  • <%= link_to "Sign Out", "/logout", id: "Logout" %> diff --git a/app/views/layouts/_mobilemenu.html.erb b/app/views/layouts/_mobilemenu.html.erb index e012a808..c557f516 100644 --- a/app/views/layouts/_mobilemenu.html.erb +++ b/app/views/layouts/_mobilemenu.html.erb @@ -2,7 +2,11 @@
    <%= yield(:mobile_title) %>
    - +
      @@ -49,6 +53,12 @@
    • <%= link_to "Account", edit_user_url(current_user) %>
    • +
    • + <%= link_to "Notifications", notifications_path %> + <% if user_has_unread_notifications? %> +
      + <% end %> +
    • <%= link_to "Sign Out", "/logout", id: "Logout" %>
    • diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index 63de15fd..cc484272 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -71,6 +71,17 @@ <% end %> + <% if current_user.present? %> + <%= link_to notifications_path, target: '_blank', class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> +
      + Notifications +
      + <% if user_has_unread_notifications? %> +
      + <% end %> + <% end %> + <% end %> + <% if !(controller_name == "sessions" && action_name == "new") %>
      diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 231c76e3..d48ec0a1 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -64,9 +64,13 @@

      <% if devise_error_messages? %> <%= devise_error_messages! %> - <% elsif notice %> + <% end %> + <% if notice %> <%= notice %> <% end %> + <% if alert %> + <%= alert %> + <% end %>

      diff --git a/app/views/layouts/doorkeeper.html.erb b/app/views/layouts/doorkeeper.html.erb index 500a0f0e..a6a74f41 100644 --- a/app/views/layouts/doorkeeper.html.erb +++ b/app/views/layouts/doorkeeper.html.erb @@ -50,6 +50,7 @@ <% end %>

      + <%= render partial: 'shared/back_to_mapping' %>
    <% end %> diff --git a/app/views/mailboxer/message_mailer/new_message_email.html.erb b/app/views/mailboxer/message_mailer/new_message_email.html.erb new file mode 100644 index 00000000..c779e888 --- /dev/null +++ b/app/views/mailboxer/message_mailer/new_message_email.html.erb @@ -0,0 +1,20 @@ + + + + + + +

    You have a new message: <%= @subject %>

    +

    + You have received a new message: +

    +
    +

    + <%= raw @message.body %> +

    +
    +

    + Visit <%= link_to root_url, root_url %> and go to your inbox for more info. +

    + + diff --git a/app/views/mailboxer/message_mailer/new_message_email.text.erb b/app/views/mailboxer/message_mailer/new_message_email.text.erb new file mode 100644 index 00000000..228ca58a --- /dev/null +++ b/app/views/mailboxer/message_mailer/new_message_email.text.erb @@ -0,0 +1,10 @@ +You have a new message: <%= @subject %> +=============================================== + +You have received a new message: + +----------------------------------------------- +<%= @message.body.html_safe? ? @message.body : strip_tags(@message.body) %> +----------------------------------------------- + +Visit <%= root_url %> and go to your inbox for more info. diff --git a/app/views/mailboxer/message_mailer/reply_message_email.html.erb b/app/views/mailboxer/message_mailer/reply_message_email.html.erb new file mode 100644 index 00000000..fd1286c5 --- /dev/null +++ b/app/views/mailboxer/message_mailer/reply_message_email.html.erb @@ -0,0 +1,20 @@ + + + + + + +

    You have a new reply: <%= @subject %>

    +

    + You have received a new reply: +

    +
    +

    + <%= raw @message.body %> +

    +
    +

    + Visit <%= link_to root_url, root_url %> and go to your inbox for more info. +

    + + diff --git a/app/views/mailboxer/message_mailer/reply_message_email.text.erb b/app/views/mailboxer/message_mailer/reply_message_email.text.erb new file mode 100644 index 00000000..c56bfb5e --- /dev/null +++ b/app/views/mailboxer/message_mailer/reply_message_email.text.erb @@ -0,0 +1,10 @@ +You have a new reply: <%= @subject %> +=============================================== + +You have received a new reply: + +----------------------------------------------- +<%= @message.body.html_safe? ? @message.body : strip_tags(@message.body) %> +----------------------------------------------- + +Visit <%= root_url %> and go to your inbox for more info. diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb new file mode 100644 index 00000000..23ee4087 --- /dev/null +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -0,0 +1,10 @@ + + + + + + + <% binding.pry %> + <%= raw @notification.body.parts[1].encoded %> + + diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb new file mode 100644 index 00000000..fa39d477 --- /dev/null +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -0,0 +1,2 @@ +<% body = @notification.body.parts[0].encoded %> +<%= body.html_safe? ? body : strip_tags(body) %> diff --git a/app/views/map_mailer/access_request_email.html.erb b/app/views/map_mailer/access_request_email.html.erb index ae4f4018..2e89eb6e 100644 --- a/app/views/map_mailer/access_request_email.html.erb +++ b/app/views/map_mailer/access_request_email.html.erb @@ -1,23 +1,16 @@ - - - - - - +
    + <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    -

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    +

    <%= @map.name %>

    -

    <%= @map.name %>

    +

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> +

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    -

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> -

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    + <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> +

    Make sense with Metamaps

    -

    Make sense with Metamaps

    -
    - - + <%= render partial: 'shared/mailer_unsubscribe_link' %> +
    diff --git a/app/views/map_mailer/access_request_email.text.erb b/app/views/map_mailer/access_request_email.text.erb index ad698d9e..25ccb2bd 100644 --- a/app/views/map_mailer/access_request_email.text.erb +++ b/app/views/map_mailer/access_request_email.text.erb @@ -7,4 +7,4 @@ Decline [<%= deny_access_map_url(id: @map.id, request_id: @request.id) %>] Make sense with Metamaps - +<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/map_mailer/invite_to_edit_email.html.erb b/app/views/map_mailer/invite_to_edit_email.html.erb index 73067c48..cd2b7b2e 100644 --- a/app/views/map_mailer/invite_to_edit_email.html.erb +++ b/app/views/map_mailer/invite_to_edit_email.html.erb @@ -1,22 +1,15 @@ - - - - - - +
    + <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    +

    <%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    + <% if @map.desc %> +

    <%= @map.desc %>

    + <% end %> -

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    -

    <%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    - <% if @map.desc %> -

    <%= @map.desc %>

    - <% end %> + <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> +

    Make sense with Metamaps

    -

    Make sense with Metamaps

    -
    - - + <%= render partial: 'shared/mailer_unsubscribe_link' %> +
    diff --git a/app/views/map_mailer/invite_to_edit_email.text.erb b/app/views/map_mailer/invite_to_edit_email.text.erb index 80eecfed..7d3bf397 100644 --- a/app/views/map_mailer/invite_to_edit_email.text.erb +++ b/app/views/map_mailer/invite_to_edit_email.text.erb @@ -4,4 +4,4 @@ Make sense with Metamaps - +<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/notifications/_header.html.erb b/app/views/notifications/_header.html.erb new file mode 100644 index 00000000..f93f46a6 --- /dev/null +++ b/app/views/notifications/_header.html.erb @@ -0,0 +1,18 @@ +
    + +
    +

    + <% if devise_error_messages? %> + <%= devise_error_messages! %> + <% elsif notice %> + <%= notice %> + <% end %> +

    diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb new file mode 100644 index 00000000..59ac08a3 --- /dev/null +++ b/app/views/notifications/index.html.erb @@ -0,0 +1,33 @@ +<% content_for :title, 'Notifications | Metamaps' %> +<% content_for :mobile_title, 'Notifications' %> + +
    +
    +

    My Notifications

    +
      + <% @notifications.each do |notification| %> + <% receipt = @receipts.find_by(notification_id: notification.id) %> +
    • + <%= link_to notification_path(notification.id) do %> +
      + <%= notification.subject %> +
      +
      + <%= notification.body.truncate(70) %> +
      + <% end %> +
      + <% if receipt.is_read? %> + <%= link_to 'mark as unread', mark_unread_notification_path(notification.id), remote: true, method: :put %> + <% else %> + <%= link_to 'mark as read', mark_read_notification_path(notification.id), remote: true, method: :put %> + <% end %> +
      +
    • + <% end %> +
    +
    + <%= render partial: 'shared/back_to_mapping' %> +
    + +<%= render partial: 'notifications/header' %> diff --git a/app/views/notifications/mark_read.js.erb b/app/views/notifications/mark_read.js.erb new file mode 100644 index 00000000..5b28f453 --- /dev/null +++ b/app/views/notifications/mark_read.js.erb @@ -0,0 +1,6 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as unread') + .attr('href', '<%= mark_unread_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('unread') + .addClass('read') diff --git a/app/views/notifications/mark_unread.js.erb b/app/views/notifications/mark_unread.js.erb new file mode 100644 index 00000000..46744388 --- /dev/null +++ b/app/views/notifications/mark_unread.js.erb @@ -0,0 +1,6 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as read') + .attr('href', '<%= mark_read_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('read') + .addClass('unread') diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb new file mode 100644 index 00000000..be641f66 --- /dev/null +++ b/app/views/notifications/show.html.erb @@ -0,0 +1,15 @@ +<% content_for :title, 'Notifications | Metamaps' %> +<% content_for :mobile_title, 'Notifications' %> + +
    +
    +

    <%= @notification.subject %>

    + <%= @notification.body %> +
    + <%= link_to 'Back', notifications_path %> +
    +
    + <%= render partial: 'shared/back_to_mapping' %> +
    + +<%= render partial: 'notifications/header' %> diff --git a/app/views/shared/_back_to_mapping.html.erb b/app/views/shared/_back_to_mapping.html.erb new file mode 100644 index 00000000..682a71e6 --- /dev/null +++ b/app/views/shared/_back_to_mapping.html.erb @@ -0,0 +1,3 @@ + diff --git a/app/views/shared/_mailer_unsubscribe_link.html.erb b/app/views/shared/_mailer_unsubscribe_link.html.erb new file mode 100644 index 00000000..2bbe7a89 --- /dev/null +++ b/app/views/shared/_mailer_unsubscribe_link.html.erb @@ -0,0 +1,3 @@ + diff --git a/app/views/shared/_mailer_unsubscribe_link.text.erb b/app/views/shared/_mailer_unsubscribe_link.text.erb new file mode 100644 index 00000000..3a2f7d0d --- /dev/null +++ b/app/views/shared/_mailer_unsubscribe_link.text.erb @@ -0,0 +1,5 @@ + + +You can unsubscribe from all Metamaps emails by visiting the following link: + +<%= unsubscribe_notifications_url %> diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 92890a92..8427582a 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -33,23 +33,35 @@
    <%= @user.name %>
    - <%= form.label :name, "Name:", :class => "firstFieldText" %> - <%= form.text_field :name %> + <%= form.label :name, "Name:", class: 'firstFieldText' %> + <%= form.text_field :name %> +
    +
    + <%= form.label :email, "Email:", class: 'firstFieldText' %> + <%= form.email_field :email %> +
    +
    + <%= form.label :emails_allowed, class: 'firstFieldText' do %> + <%= form.check_box :emails_allowed, class: 'inline' %> + Send Metamaps notifications to my email. + <% end %>
    -
    <%= form.label :email, "Email:", :class => "firstFieldText" %> - <%= form.email_field :email %>
    Change Password
    -
    - <%= form.label :current_password, "Current Password:", :class => "firstFieldText" %> - <%= password_field_tag :current_password, params[:current_password] %> +
    + <%= form.label :current_password, "Current Password:", :class => "firstFieldText" %> + <%= password_field_tag :current_password, params[:current_password] %> +
    +
    + <%= form.label :password, "New Password:", :class => "firstFieldText" %> + <%= form.password_field :password, :autocomplete => :off%> +
    +
    + <%= form.label :password_confirmation, "Confirm New Password:", :class => "firstFieldText" %> + <%= form.password_field :password_confirmation, :autocomplete => :off%> +
    +
    Oops, don't change password
    -
    <%= form.label :password, "New Password:", :class => "firstFieldText" %> - <%= form.password_field :password, :autocomplete => :off%>
    -
    <%= form.label :password_confirmation, "Confirm New Password:", :class => "firstFieldText" %> - <%= form.password_field :password_confirmation, :autocomplete => :off%>
    -
    Oops, don't change password
    -
    <%= form.submit "Update", class: "update", onclick: "Metamaps.Account.showLoading()" %>
    diff --git a/config/application.rb b/config/application.rb index 0b98bfe8..ff5a621c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -8,14 +8,15 @@ Bundler.require(*Rails.groups) module Metamaps class Application < Rails::Application - config.active_job.queue_adapter = :delayed_job - if ENV['ACTIVE_JOB_FRAMEWORK'] == 'sucker_punch' - config.active_job.queue_adapter = :sucker_punch - end - # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + # + config.active_job.queue_adapter = if ENV['ACTIVE_JOB_FRAMEWORK'] == 'sucker_punch' + :sucker_punch + else + :delayed_job + end # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths << Rails.root.join('app', 'services') diff --git a/config/environments/development.rb b/config/environments/development.rb index 38741a18..8fef2145 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,19 +14,11 @@ Rails.application.configure do config.consider_all_requests_local = true config.action_controller.perform_caching = false - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - address: ENV['SMTP_SERVER'], - port: ENV['SMTP_PORT'], - user_name: ENV['SMTP_USERNAME'], - password: ENV['SMTP_PASSWORD'], - domain: ENV['SMTP_DOMAIN'], - authentication: 'plain', - enable_starttls_auto: true, - openssl_verify_mode: 'none' + config.action_mailer.delivery_method = :file + config.action_mailer.file_settings = { + location: 'tmp/mails' } config.action_mailer.default_url_options = { host: 'localhost:3000' } - # Don't care if the mailer can't send config.action_mailer.raise_delivery_errors = true # Print deprecation notices to the Rails logger diff --git a/config/initializers/mailboxer.rb b/config/initializers/mailboxer.rb new file mode 100644 index 00000000..9e1efe66 --- /dev/null +++ b/config/initializers/mailboxer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +Mailboxer.setup do |config| + # Configures if your application uses or not email sending for Notifications and Messages + config.uses_emails = true + + # Configures the default from for emails sent for Messages and Notifications + config.default_from = 'no-reply@metamaps.cc' + + # Configures the methods needed by mailboxer + config.email_method = :mailboxer_email + config.name_method = :name + + # Configures if you use or not a search engine and which one you are using + # Supported engines: [:solr,:sphinx] + config.search_enabled = false + config.search_engine = :solr + + # Configures maximum length of the message subject and body + config.subject_max_length = 255 + config.body_max_length = 32_000 +end diff --git a/config/routes.rb b/config/routes.rb index 8ba116a1..000784f6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,12 +20,25 @@ Metamaps::Application.routes.draw do post 'events/:event', action: :events get :contains - get :request_access, to: 'access#request_access' - get 'approve_access/:request_id', to: 'access#approve_access', as: :approve_access - get 'deny_access/:request_id', to: 'access#deny_access', as: :deny_access - post :access_request, to: 'access#access_request', default: { format: :json } - post 'approve_access/:request_id', to: 'access#approve_access_post', default: { format: :json } - post 'deny_access/:request_id', to: 'access#deny_access_post', default: { format: :json } + get :request_access, + to: 'access#request_access' + get 'approve_access/:request_id', + to: 'access#approve_access', + as: :approve_access + get 'deny_access/:request_id', + to: 'access#deny_access', + as: :deny_access + + post :access_request, + to: 'access#access_request', + default: { format: :json } + post 'approve_access/:request_id', + to: 'access#approve_access_post', + default: { format: :json } + post 'deny_access/:request_id', + to: 'access#deny_access_post', + default: { format: :json } + post :access, to: 'access#access', default: { format: :json } post :star, to: 'stars#create', default: { format: :json } @@ -36,6 +49,15 @@ Metamaps::Application.routes.draw do resources :mappings, except: [:index, :new, :edit] resources :messages, only: [:show, :create, :update, :destroy] + resources :notifications, only: [:index, :show] do + collection do + get :unsubscribe + end + member do + put :mark_read + put :mark_unread + end + end resources :metacode_sets, except: [:show] @@ -109,3 +131,4 @@ Metamaps::Application.routes.draw do get 'load_url_title' end end +# rubocop:enable Rubocop/Metrics/BlockLength diff --git a/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb b/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb new file mode 100644 index 00000000..99ed59b1 --- /dev/null +++ b/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb @@ -0,0 +1,65 @@ +# This migration comes from mailboxer_engine (originally 20110511145103) +class CreateMailboxer < ActiveRecord::Migration + def self.up + #Tables + #Conversations + create_table :mailboxer_conversations do |t| + t.column :subject, :string, :default => "" + t.column :created_at, :datetime, :null => false + t.column :updated_at, :datetime, :null => false + end + #Receipts + create_table :mailboxer_receipts do |t| + t.references :receiver, :polymorphic => true + t.column :notification_id, :integer, :null => false + t.column :is_read, :boolean, :default => false + t.column :trashed, :boolean, :default => false + t.column :deleted, :boolean, :default => false + t.column :mailbox_type, :string, :limit => 25 + t.column :created_at, :datetime, :null => false + t.column :updated_at, :datetime, :null => false + end + #Notifications and Messages + create_table :mailboxer_notifications do |t| + t.column :type, :string + t.column :body, :text + t.column :subject, :string, :default => "" + t.references :sender, :polymorphic => true + t.column :conversation_id, :integer + t.column :draft, :boolean, :default => false + t.string :notification_code, :default => nil + t.references :notified_object, :polymorphic => true + t.column :attachment, :string + t.column :updated_at, :datetime, :null => false + t.column :created_at, :datetime, :null => false + t.boolean :global, default: false + t.datetime :expires + end + + #Indexes + #Conversations + #Receipts + add_index "mailboxer_receipts","notification_id" + + #Messages + add_index "mailboxer_notifications","conversation_id" + + #Foreign keys + #Conversations + #Receipts + add_foreign_key "mailboxer_receipts", "mailboxer_notifications", :name => "receipts_on_notification_id", :column => "notification_id" + #Messages + add_foreign_key "mailboxer_notifications", "mailboxer_conversations", :name => "notifications_on_conversation_id", :column => "conversation_id" + end + + def self.down + #Tables + remove_foreign_key "mailboxer_receipts", :name => "receipts_on_notification_id" + remove_foreign_key "mailboxer_notifications", :name => "notifications_on_conversation_id" + + #Indexes + drop_table :mailboxer_receipts + drop_table :mailboxer_conversations + drop_table :mailboxer_notifications + end +end diff --git a/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb b/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb new file mode 100644 index 00000000..c4f4555a --- /dev/null +++ b/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb @@ -0,0 +1,15 @@ +# This migration comes from mailboxer_engine (originally 20131206080416) +class AddConversationOptout < ActiveRecord::Migration + def self.up + create_table :mailboxer_conversation_opt_outs do |t| + t.references :unsubscriber, :polymorphic => true + t.references :conversation + end + add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", :name => "mb_opt_outs_on_conversations_id", :column => "conversation_id" + end + + def self.down + remove_foreign_key "mailboxer_conversation_opt_outs", :name => "mb_opt_outs_on_conversations_id" + drop_table :mailboxer_conversation_opt_outs + end +end diff --git a/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb b/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb new file mode 100644 index 00000000..fde96718 --- /dev/null +++ b/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb @@ -0,0 +1,20 @@ +# This migration comes from mailboxer_engine (originally 20131206080417) +class AddMissingIndices < ActiveRecord::Migration + def change + # We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63 + # characters limitation. + add_index :mailboxer_conversation_opt_outs, [:unsubscriber_id, :unsubscriber_type], + name: 'index_mailboxer_conversation_opt_outs_on_unsubscriber_id_type' + add_index :mailboxer_conversation_opt_outs, :conversation_id + + add_index :mailboxer_notifications, :type + add_index :mailboxer_notifications, [:sender_id, :sender_type] + + # We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63 + # characters limitation. + add_index :mailboxer_notifications, [:notified_object_id, :notified_object_type], + name: 'index_mailboxer_notifications_on_notified_object_id_and_type' + + add_index :mailboxer_receipts, [:receiver_id, :receiver_type] + end +end diff --git a/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb b/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb new file mode 100644 index 00000000..a820919e --- /dev/null +++ b/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb @@ -0,0 +1,8 @@ +# This migration comes from mailboxer_engine (originally 20151103080417) +class AddDeliveryTrackingInfoToMailboxerReceipts < ActiveRecord::Migration + def change + add_column :mailboxer_receipts, :is_delivered, :boolean, default: false + add_column :mailboxer_receipts, :delivery_method, :string + add_column :mailboxer_receipts, :message_id, :string + end +end diff --git a/db/migrate/20161125175229_add_emails_allowed_to_users.rb b/db/migrate/20161125175229_add_emails_allowed_to_users.rb new file mode 100644 index 00000000..609e4309 --- /dev/null +++ b/db/migrate/20161125175229_add_emails_allowed_to_users.rb @@ -0,0 +1,5 @@ +class AddEmailsAllowedToUsers < ActiveRecord::Migration[5.0] + def change + add_column :users, :emails_allowed, :boolean, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index d16d4fb9..5839929c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161105160340) do +ActiveRecord::Schema.define(version: 20161125175229) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -63,6 +63,59 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.index ["metacode_set_id"], name: "index_in_metacode_sets_on_metacode_set_id", using: :btree end + create_table "mailboxer_conversation_opt_outs", force: :cascade do |t| + t.string "unsubscriber_type" + t.integer "unsubscriber_id" + t.integer "conversation_id" + t.index ["conversation_id"], name: "index_mailboxer_conversation_opt_outs_on_conversation_id", using: :btree + t.index ["unsubscriber_id", "unsubscriber_type"], name: "index_mailboxer_conversation_opt_outs_on_unsubscriber_id_type", using: :btree + end + + create_table "mailboxer_conversations", force: :cascade do |t| + t.string "subject", default: "" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "mailboxer_notifications", force: :cascade do |t| + t.string "type" + t.text "body" + t.string "subject", default: "" + t.string "sender_type" + t.integer "sender_id" + t.integer "conversation_id" + t.boolean "draft", default: false + t.string "notification_code" + t.string "notified_object_type" + t.integer "notified_object_id" + t.string "attachment" + t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.boolean "global", default: false + t.datetime "expires" + t.index ["conversation_id"], name: "index_mailboxer_notifications_on_conversation_id", using: :btree + t.index ["notified_object_id", "notified_object_type"], name: "index_mailboxer_notifications_on_notified_object_id_and_type", using: :btree + t.index ["sender_id", "sender_type"], name: "index_mailboxer_notifications_on_sender_id_and_sender_type", using: :btree + t.index ["type"], name: "index_mailboxer_notifications_on_type", using: :btree + end + + create_table "mailboxer_receipts", force: :cascade do |t| + t.string "receiver_type" + t.integer "receiver_id" + t.integer "notification_id", null: false + t.boolean "is_read", default: false + t.boolean "trashed", default: false + t.boolean "deleted", default: false + t.string "mailbox_type", limit: 25 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "is_delivered", default: false + t.string "delivery_method" + t.string "message_id" + t.index ["notification_id"], name: "index_mailboxer_receipts_on_notification_id", using: :btree + t.index ["receiver_id", "receiver_type"], name: "index_mailboxer_receipts_on_receiver_id_and_receiver_type", using: :btree + end + create_table "mappings", force: :cascade do |t| t.text "category" t.integer "xloc" @@ -243,8 +296,8 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.string "password_salt", limit: 255 t.string "persistence_token", limit: 255 t.string "perishable_token", limit: 255 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "code", limit: 8 t.string "joinedwithcode", limit: 8 t.text "settings" @@ -264,6 +317,7 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.integer "image_file_size" t.datetime "image_updated_at" t.integer "generation" + t.boolean "emails_allowed", default: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree end @@ -279,5 +333,8 @@ ActiveRecord::Schema.define(version: 20161105160340) do add_foreign_key "access_requests", "maps" add_foreign_key "access_requests", "users" + add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", column: "conversation_id", name: "mb_opt_outs_on_conversations_id" + add_foreign_key "mailboxer_notifications", "mailboxer_conversations", column: "conversation_id", name: "notifications_on_conversation_id" + add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id" add_foreign_key "tokens", "users" end diff --git a/doc/metamaps-qa-steps.md b/doc/metamaps-qa-steps.md index a3f136ca..7c5c8480 100644 --- a/doc/metamaps-qa-steps.md +++ b/doc/metamaps-qa-steps.md @@ -32,6 +32,13 @@ Run these tests to be reasonably sure that your code changes haven't broken anyt - Add a number of synapses to one of your maps. Reload to see if they are still there. - Rearrange one of your maps and save the layout. Reload to see if the layout is preserved. +### Unsubscribing from Notifications + + - Log out + - Visit /notifications/unsubscribe. It should redirect you to the login page. + - Log in. + - It should redirect you to the user edit page, and you should be unsubscribed. + ### Misc - Login as admin. Change metacode sets. From c46e85529eb35425cd2a281e5f13f94d45ff38b6 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 28 Nov 2016 17:37:27 -0500 Subject: [PATCH 02/16] little style tweaks to css and content --- app/assets/stylesheets/apps.css.erb | 4 ++-- app/assets/stylesheets/clean.css.erb | 20 +++++++++++++------ app/assets/stylesheets/notifications.scss.erb | 8 ++++---- app/views/layouts/_upperelements.html.erb | 2 +- app/views/notifications/index.html.erb | 3 +++ 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/apps.css.erb b/app/assets/stylesheets/apps.css.erb index a88199af..f3f444c7 100644 --- a/app/assets/stylesheets/apps.css.erb +++ b/app/assets/stylesheets/apps.css.erb @@ -131,9 +131,9 @@ } .back-to-mapping { - margin: 1em; + margin: 1em auto; width: auto; - max-width: 100%; + max-width: 960px; box-sizing: border-box; } diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index b25816f0..8e0e7970 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -215,6 +215,9 @@ background-position: -128px 0; margin-right: 10px; // make it look more natural next to the account menu icon } +.notificationsIcon:hover { + background-position: -128px -32px; +} .importDialog:hover { background-position: 0 -32px; } @@ -226,7 +229,6 @@ } .addMap:hover { background-position: -96px -32px; - margin-right:10px; } @@ -474,7 +476,7 @@ background-position: -32px 0; } -.zoomExtents:hover .tooltips, .zoomIn:hover .tooltips, .zoomOut:hover .tooltips, .takeScreenshot:hover .tooltips, .sidebarFilterIcon:hover .tooltipsUnder, .sidebarForkIcon:hover .tooltipsUnder, .addMap:hover .tooltipsUnder, .authenticated .sidebarAccountIcon:hover .tooltipsUnder, +.zoomExtents:hover .tooltips, .zoomIn:hover .tooltips, .zoomOut:hover .tooltips, .takeScreenshot:hover .tooltips, .sidebarFilterIcon:hover .tooltipsUnder, .sidebarForkIcon:hover .tooltipsUnder, .notificationsIcon:hover .tooltipsUnder, .addMap:hover .tooltipsUnder, .authenticated .sidebarAccountIcon:hover .tooltipsUnder, .mapInfoIcon:hover .tooltipsAbove, .openCheatsheet:hover .tooltipsAbove, .chat-button:hover .tooltips, .importDialog:hover .tooltipsUnder, .starMap:hover .tooltipsAbove, .openMetacodeSwitcher:hover .tooltipsAbove, .pinCarousel:not(.isPinned):hover .tooltipsAbove.helpPin, .pinCarousel.isPinned:hover .tooltipsAbove.helpUnpin { display: block; } @@ -538,6 +540,9 @@ .sidebarFilterIcon .tooltipsUnder { margin-left: -4px; } +.notificationsIcon .tooltipsUnder { + left: -20px; +} .sidebarForkIcon .tooltipsUnder { margin-left: -34px; @@ -615,7 +620,7 @@ border-bottom: 5px solid transparent; } -.importDialog div:after, .sidebarFilterIcon div:after, .sidebarForkIcon div:after, .addMap div:after, .sidebarAccountIcon .tooltipsUnder:after { +.importDialog div:after, .sidebarFilterIcon div:after, .sidebarForkIcon div:after, .addMap div:after, .sidebarAccountIcon .tooltipsUnder:after, .notificationsIcon div:after { content: ''; position: absolute; right: 40%; @@ -629,6 +634,9 @@ .sidebarFilterIcon div:after { right: 37% !important; } +.notificationsIcon div:after { + right: 46% !important; +} .mapInfoIcon div:after, .openCheatsheet div:after, .starMap div:after, .openMetacodeSwitcher div:after, .pinCarousel div:after { content: ''; @@ -785,8 +793,8 @@ background-position: -96px 0; } .exploreMapsCenter .notificationsLink .exploreMapsIcon { - background-image: url(<%= asset_path 'user_sprite.png' %>); - background-position: 0 -128px; + background-image: url(<%= asset_path 'topright_sprite.png' %>); + background-position: -128px 0; } .authedApps:hover .exploreMapsIcon, .authedApps.active .exploreMapsIcon { background-position-x: -32px; @@ -807,7 +815,7 @@ background-position: -128px -32px; } .notificationsLink:hover .exploreMapsIcon, .notificationsLink.active .exploreMapsIcon { - background-position-x: -32px; + background-position-y: -32px; } .mapsWrapper { diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index b25bf4e8..a866af04 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -24,10 +24,11 @@ $unread_notifications_dot_size: 8px; .notificationPage, .notificationsPage { width: auto; - max-width: 100%; + max-width: 960px; box-sizing: border-box; - margin: 1em; + margin: 1em auto; margin-top: 1em + $menu_bar_height; + font-family: 'din-regular', Sans-Serif; & > .title { border-bottom: 1px solid #eee; @@ -57,14 +58,13 @@ $unread_notifications_dot_size: 8px; .notification-read-unread { display: inline-block; vertical-align: top; - font-weight: 300; } &.unread { .notification-body, .notification-subject, .notification-read-unread { - font-weight: bold; + font-family: 'din-medium', Sans-Serif; } } } diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index cc484272..8b2882ce 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -72,7 +72,7 @@ <% end %> <% if current_user.present? %> - <%= link_to notifications_path, target: '_blank', class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> + <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %>
    Notifications
    diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index 59ac08a3..eb576ce6 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -25,6 +25,9 @@
  • <% end %> + <% if @notifications.count == 0 %> + You have ZERO unread notifications. Huzzah! + <% end %> <%= render partial: 'shared/back_to_mapping' %> From 9b95e91f1aea63d1e14254a5b89cfed045bedc10 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Tue, 29 Nov 2016 11:15:14 -0500 Subject: [PATCH 03/16] more style tweaks + brakeman fix --- app/assets/stylesheets/clean.css.erb | 10 +++++++- app/controllers/access_controller.rb | 4 ++-- .../new_notification_email.html.erb | 3 +-- .../new_notification_email.text.erb | 3 +-- app/views/notifications/index.html.erb | 2 +- app/views/notifications/show.html.erb | 2 +- config/brakeman.ignore | 24 +++++++++++++++++++ 7 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 config/brakeman.ignore diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index 8e0e7970..f4352504 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -620,7 +620,12 @@ border-bottom: 5px solid transparent; } -.importDialog div:after, .sidebarFilterIcon div:after, .sidebarForkIcon div:after, .addMap div:after, .sidebarAccountIcon .tooltipsUnder:after, .notificationsIcon div:after { +.addMap div:after, +.importDialog div:after, +.sidebarForkIcon div:after, +.sidebarFilterIcon div:after, +.notificationsIcon div:after, +.sidebarAccountIcon .tooltipsUnder:after, content: ''; position: absolute; right: 40%; @@ -631,6 +636,9 @@ border-left: 5px solid transparent; border-right: 5px solid transparent; } +.notificationsIcon .unread-notifications-dot:after { + content: none; +} .sidebarFilterIcon div:after { right: 37% !important; } diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index a0271981..5f19d23e 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -22,7 +22,7 @@ class AccessController < ApplicationController request = AccessRequest.create(user: current_user, map: @map) # what about push notification to map owner? mail = MapMailer.access_request_email(request, @map) - @map.user.notify(mail.subject, mail.body) + @map.user.notify(mail.subject, mail.body.parts[1].body.to_s) respond_to do |format| format.json do @@ -40,7 +40,7 @@ class AccessController < ApplicationController # who we then send an email to user = User.find(user_id) mail = MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)) - user.notify(mail.subject, mail.body) + user.notify(mail.subject, mail.body.parts[1].body.to_s) end @map.remove_old_collaborators(user_ids) diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb index 23ee4087..ac4af493 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -4,7 +4,6 @@ - <% binding.pry %> - <%= raw @notification.body.parts[1].encoded %> + <%= raw @notification.body %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb index fa39d477..1c230d08 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -1,2 +1 @@ -<% body = @notification.body.parts[0].encoded %> -<%= body.html_safe? ? body : strip_tags(body) %> +<%= @notification.body.html_safe? ? @notification.body : strip_tags(@notification.body) %> diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index eb576ce6..bd8022e5 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -13,7 +13,7 @@ <%= notification.subject %>
    - <%= notification.body.truncate(70) %> + <%= strip_tags(notification.body).truncate(70) %>
    <% end %>
    diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index be641f66..1d61ccc3 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -4,7 +4,7 @@

    <%= @notification.subject %>

    - <%= @notification.body %> + <%= raw @notification.body %>
    <%= link_to 'Back', notifications_path %>
    diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 00000000..9e29ff0d --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,24 @@ +{ + "ignored_warnings": [ + { + "warning_type": "Cross Site Scripting", + "warning_code": 2, + "fingerprint": "88694dca0bcc2226859746f9ed40cc682d6e5eaec1e73f2be557770a854ede0b", + "message": "Unescaped model attribute", + "file": "app/views/notifications/show.html.erb", + "line": 7, + "link": "http://brakemanscanner.org/docs/warning_types/cross_site_scripting", + "code": "current_user.mailbox.notifications.find_by(:id => params[:id]).body", + "render_path": [{"type":"controller","class":"NotificationsController","method":"show","line":24,"file":"app/controllers/notifications_controller.rb"}], + "location": { + "type": "template", + "template": "notifications/show" + }, + "user_input": "current_user.mailbox.notifications", + "confidence": "Weak", + "note": "" + } + ], + "updated": "2016-11-29 13:01:34 -0500", + "brakeman_version": "3.4.0" +} From b4ad51e69d2c4534427e14791cd438f57ec74910 Mon Sep 17 00:00:00 2001 From: Robert Best Date: Sun, 4 Dec 2016 20:02:24 +0000 Subject: [PATCH 04/16] reactify notification icon --- app/views/layouts/_upperelements.html.erb | 19 +++++---- app/views/notifications/mark_read.js.erb | 1 + app/views/notifications/mark_unread.js.erb | 1 + .../src/Metamaps/GlobalUI/NotificationIcon.js | 30 ++++++++++++++ frontend/src/Metamaps/GlobalUI/index.js | 4 +- frontend/src/Metamaps/index.js | 4 +- frontend/src/components/NotificationIcon.js | 39 +++++++++++++++++++ 7 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 frontend/src/Metamaps/GlobalUI/NotificationIcon.js create mode 100644 frontend/src/components/NotificationIcon.js diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index 8b2882ce..515bfcd4 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -71,15 +71,20 @@ <% end %> + <% if current_user.present? %> - <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> -
    - Notifications -
    - <% if user_has_unread_notifications? %> -
    + + <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> +
    + Notifications +
    + <% if user_has_unread_notifications? %> +
    + <% end %> <% end %> - <% end %> +
    <% end %> diff --git a/app/views/notifications/mark_read.js.erb b/app/views/notifications/mark_read.js.erb index 5b28f453..cbf2cf13 100644 --- a/app/views/notifications/mark_read.js.erb +++ b/app/views/notifications/mark_read.js.erb @@ -4,3 +4,4 @@ $('#notification-<%= @notification.id %> .notification-read-unread > a') $('#notification-<%= @notification.id %>') .removeClass('unread') .addClass('read') +Metamaps.GlobalUI.NotificationIcon.render(Metamaps.GlobalUI.NotificationIcon.unreadNotificationsCount - 1) \ No newline at end of file diff --git a/app/views/notifications/mark_unread.js.erb b/app/views/notifications/mark_unread.js.erb index 46744388..3fffab24 100644 --- a/app/views/notifications/mark_unread.js.erb +++ b/app/views/notifications/mark_unread.js.erb @@ -4,3 +4,4 @@ $('#notification-<%= @notification.id %> .notification-read-unread > a') $('#notification-<%= @notification.id %>') .removeClass('read') .addClass('unread') +Metamaps.GlobalUI.NotificationIcon.render(Metamaps.GlobalUI.NotificationIcon.unreadNotificationsCount + 1) \ No newline at end of file diff --git a/frontend/src/Metamaps/GlobalUI/NotificationIcon.js b/frontend/src/Metamaps/GlobalUI/NotificationIcon.js new file mode 100644 index 00000000..2f13adba --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/NotificationIcon.js @@ -0,0 +1,30 @@ +/* global $ */ + +import React from 'react' +import ReactDOM from 'react-dom' + +import Active from '../Active' +import NotificationIconComponent from '../../components/NotificationIcon' + +const NotificationIcon = { + unreadNotificationsCount: null, + + init: function(serverData) { + const self = NotificationIcon + self.unreadNotificationsCount = serverData.unreadNotificationsCount + self.render() + }, + render: function(newUnreadCount = null) { + if (newUnreadCount !== null) { + NotificationIcon.unreadNotificationsCount = newUnreadCount + } + + if (Active.Mapper !== null) { + ReactDOM.render(React.createElement(NotificationIconComponent, { + unreadNotificationsCount: NotificationIcon.unreadNotificationsCount + }), $('#notification_icon').get(0)) + } + } +} + +export default NotificationIcon \ No newline at end of file diff --git a/frontend/src/Metamaps/GlobalUI/index.js b/frontend/src/Metamaps/GlobalUI/index.js index 95e484f8..932e3319 100644 --- a/frontend/src/Metamaps/GlobalUI/index.js +++ b/frontend/src/Metamaps/GlobalUI/index.js @@ -8,6 +8,7 @@ import Search from './Search' import CreateMap from './CreateMap' import Account from './Account' import ImportDialog from './ImportDialog' +import NotificationIcon from './NotificationIcon' const GlobalUI = { notifyTimeout: null, @@ -19,6 +20,7 @@ const GlobalUI = { self.CreateMap.init(serverData) self.Account.init(serverData) self.ImportDialog.init(serverData, self.openLightbox, self.closeLightbox) + self.NotificationIcon.init(serverData) if ($('#toast').html().trim()) self.notifyUser($('#toast').html()) @@ -127,5 +129,5 @@ const GlobalUI = { } } -export { Search, CreateMap, Account, ImportDialog } +export { Search, CreateMap, Account, ImportDialog, NotificationIcon } export default GlobalUI diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index dfad4d95..be218aff 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -8,7 +8,8 @@ import Create from './Create' import Debug from './Debug' import Filter from './Filter' import GlobalUI, { - Search, CreateMap, ImportDialog, Account as GlobalUIAccount + Search, CreateMap, ImportDialog, Account as GlobalUIAccount, + NotificationIcon } from './GlobalUI' import Import from './Import' import JIT from './JIT' @@ -47,6 +48,7 @@ Metamaps.GlobalUI.Search = Search Metamaps.GlobalUI.CreateMap = CreateMap Metamaps.GlobalUI.Account = GlobalUIAccount Metamaps.GlobalUI.ImportDialog = ImportDialog +Metamaps.GlobalUI.NotificationIcon = NotificationIcon Metamaps.Import = Import Metamaps.JIT = JIT Metamaps.Listeners = Listeners diff --git a/frontend/src/components/NotificationIcon.js b/frontend/src/components/NotificationIcon.js new file mode 100644 index 00000000..b886f557 --- /dev/null +++ b/frontend/src/components/NotificationIcon.js @@ -0,0 +1,39 @@ +import React, { PropTypes, Component } from 'react' + +class NotificationIcon extends Component { + constructor(props) { + super(props) + + this.state = { + } + } + + render = () => { + var linkClasses = "notificationsIcon upperRightEl upperRightIcon " + + if (this.props.unreadNotificationsCount > 0) { + linkClasses += "unread" + } else { + linkClasses += "read" + } + + return ( + +
    + Notifications +
    + {this.props.unreadNotificationsCount === 0 ? null : ( +
    + )} +
    + + + ) + } +} + +NotificationIcon.propTypes = { + unreadNotificationsCount: PropTypes.number +} + +export default NotificationIcon From 9debcdde39fb479af345ca3ebbfd0198b1d5395a Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Thu, 8 Dec 2016 14:39:41 -0500 Subject: [PATCH 05/16] Integrate rails mailers with mailboxer --- app/assets/stylesheets/notifications.scss.erb | 10 ++++++++-- app/controllers/access_controller.rb | 13 ++++--------- app/helpers/application_helper.rb | 11 +---------- app/mailers/application_mailer.rb | 12 ++++++++++++ app/views/layouts/_mobilemenu.html.erb | 4 ++-- app/views/layouts/_upperelements.html.erb | 6 +++--- .../new_notification_email.html.erb | 11 +++-------- .../new_notification_email.text.erb | 3 ++- app/views/notifications/show.html.erb | 12 ++++++++---- config/initializers/mailboxer.rb | 11 +++++++++++ frontend/src/components/NotificationIcon.js | 2 +- 11 files changed, 55 insertions(+), 40 deletions(-) diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index a866af04..d7942135 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -30,7 +30,7 @@ $unread_notifications_dot_size: 8px; margin-top: 1em + $menu_bar_height; font-family: 'din-regular', Sans-Serif; - & > .title { + & > .notification-title { border-bottom: 1px solid #eee; padding-bottom: 0.25em; margin-bottom: 0.5em; @@ -42,7 +42,7 @@ $unread_notifications_dot_size: 8px; } - .notification { + .notificationsPage .notification { .notification-subject { width: 25%; } @@ -68,4 +68,10 @@ $unread_notifications_dot_size: 8px; } } } + + .notificationPage .notification-body { + p, div { + margin: 1em auto; + } + } } diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index 5f19d23e..a287ca1b 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -22,12 +22,10 @@ class AccessController < ApplicationController request = AccessRequest.create(user: current_user, map: @map) # what about push notification to map owner? mail = MapMailer.access_request_email(request, @map) - @map.user.notify(mail.subject, mail.body.parts[1].body.to_s) + @map.user.notify(mail.subject, 'access request', request, true, MAILBOXER_CODE_ACCESS_REQUEST) respond_to do |format| - format.json do - head :ok - end + format.json { head :ok } end end @@ -38,16 +36,13 @@ class AccessController < ApplicationController @map.add_new_collaborators(user_ids).each do |user_id| # add_new_collaborators returns array of added users, # who we then send an email to - user = User.find(user_id) mail = MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)) - user.notify(mail.subject, mail.body.parts[1].body.to_s) + user.notify(mail.subject, 'invite to edit', UserMap.find_by(user_id: user_id, map: @map), true, MAILBOXER_CODE_INVITED_TO_EDIT) end @map.remove_old_collaborators(user_ids) respond_to do |format| - format.json do - head :ok - end + format.json { head :ok } end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cc121cbe..45a5d565 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -38,18 +38,9 @@ module ApplicationHelper "#{request.base_url}/join" + (current_user ? "?code=#{current_user.code}" : '') end - def user_has_unread_notifications? - return @user_has_unread_notifications unless @user_has_unread_notifications.nil? - return (@user_has_unread_notifications = false) if current_user.nil? - current_user.mailboxer_notification_receipts.each do |receipt| - return (@user_has_unread_notifications = true) if receipt.is_read == false - end - @user_has_unread_notifications = false - end - def user_unread_notification_count return 0 if current_user.nil? - current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| + @user_unread_notification_count ||= current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| receipt.is_read ? total : total + 1 end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 10961836..338f38ee 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -6,4 +6,16 @@ class ApplicationMailer < ActionMailer::Base def deliver raise NotImplementedError('Please use Mailboxer to send your emails.') end + + class << self + def mail_for_notification(notification) + if notification.notification_code == MAILBOXER_CODE_ACCESS_REQUEST + request = notification.notified_object + MapMailer.access_request_email(request, request.map) + elsif notification.notification_code == MAILBOXER_CODE_INVITED_TO_EDIT + user_map = notification.notified_object + MapMailer.invite_to_edit_email(user_map.map, user_map.map.user, user_map.user) + end + end + end end diff --git a/app/views/layouts/_mobilemenu.html.erb b/app/views/layouts/_mobilemenu.html.erb index c557f516..5ef3a66d 100644 --- a/app/views/layouts/_mobilemenu.html.erb +++ b/app/views/layouts/_mobilemenu.html.erb @@ -3,7 +3,7 @@ <%= yield(:mobile_title) %>
    @@ -55,7 +55,7 @@
  • <%= link_to "Notifications", notifications_path %> - <% if user_has_unread_notifications? %> + <% if user_unread_notification_count > 0 %>
    <% end %>
  • diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index 515bfcd4..1f499615 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -76,11 +76,11 @@ <% if current_user.present? %> - <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> + <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_unread_notification_count > 0 ? 'unread' : 'read'}" do %>
    - Notifications + Notifications (<%= user_unread_notification_count %> unread)
    - <% if user_has_unread_notifications? %> + <% if user_unread_notification_count > 0 %>
    <% end %> <% end %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb index ac4af493..d8fda23c 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -1,9 +1,4 @@ - - - - - - <%= raw @notification.body %> - - +<% mail = ApplicationMailer.mail_for_notification(@notification) %> +<% @notification.update(body: mail.html_part&.body&.decoded) %> +<%= raw mail.html_part&.body&.decoded %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb index 1c230d08..45fa8ae0 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -1 +1,2 @@ -<%= @notification.body.html_safe? ? @notification.body : strip_tags(@notification.body) %> +<% mail = ApplicationMailer.mail_for_notification(@notification) %> +<%= mail.text_part&.body&.decoded %> diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index 1d61ccc3..1c555954 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -3,12 +3,16 @@
    -

    <%= @notification.subject %>

    - <%= raw @notification.body %> -
    - <%= link_to 'Back', notifications_path %> +

    <%= @notification.subject %>

    +
    + <%= raw @notification.body %>
    + +
    + <%= link_to 'Back', notifications_path %> +
    + <%= render partial: 'shared/back_to_mapping' %>
    diff --git a/config/initializers/mailboxer.rb b/config/initializers/mailboxer.rb index 9e1efe66..115d80a4 100644 --- a/config/initializers/mailboxer.rb +++ b/config/initializers/mailboxer.rb @@ -1,4 +1,15 @@ # frozen_string_literal: true + +# notification codes to differentiate different types of notifications +# e.g. a notification might have { +# notified_object_type: 'Map', +# notified_object_id: 1, +# notification_code: MAILBOXER_CODE_ACCESS_REQUEST +# }, +# which would imply that this is an access request to Map.find(1) +MAILBOXER_CODE_ACCESS_REQUEST = 'ACCESS_REQUEST' +MAILBOXER_CODE_INVITED_TO_EDIT = 'INVITED_TO_EDIT' + Mailboxer.setup do |config| # Configures if your application uses or not email sending for Notifications and Messages config.uses_emails = true diff --git a/frontend/src/components/NotificationIcon.js b/frontend/src/components/NotificationIcon.js index b886f557..98782a75 100644 --- a/frontend/src/components/NotificationIcon.js +++ b/frontend/src/components/NotificationIcon.js @@ -20,7 +20,7 @@ class NotificationIcon extends Component { return (
    - Notifications + Notifications ({this.props.unreadNotificationsCount} unread)
    {this.props.unreadNotificationsCount === 0 ? null : (
    From 8e958ec9a80688c0efa25b22ebe25e3fba817ee7 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Fri, 9 Dec 2016 11:59:24 -0500 Subject: [PATCH 06/16] invite to edit notifications marked as read in system once map is visited --- app/controllers/maps_controller.rb | 1 + app/models/user_map.rb | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index d66456b8..6e1e0d77 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -8,6 +8,7 @@ class MapsController < ApplicationController def show respond_to do |format| format.html do + UserMap.where(map: @map, user: current_user).map(&:mark_invite_notifications_as_read) @allmappers = @map.contributors @allcollaborators = @map.editors @alltopics = policy_scope(@map.topics) diff --git a/app/models/user_map.rb b/app/models/user_map.rb index dc268047..3aa87b03 100644 --- a/app/models/user_map.rb +++ b/app/models/user_map.rb @@ -2,4 +2,10 @@ class UserMap < ApplicationRecord belongs_to :map belongs_to :user + + def mark_invite_notifications_as_read + Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) + end + end end From 3f6f020ce1c7ab6d4dcc21cac39fb66d3887ba41 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Fri, 9 Dec 2016 12:04:17 -0500 Subject: [PATCH 07/16] grant/deny buttons mark access request notifications as read --- app/controllers/access_controller.rb | 4 ++-- app/models/access_request.rb | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index a287ca1b..d3808a1a 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -49,7 +49,7 @@ class AccessController < ApplicationController # GET maps/:id/approve_access/:request_id def approve_access request = AccessRequest.find(params[:request_id]) - request.approve + request.approve # also marks mailboxer notification as read respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was approved' } end @@ -58,7 +58,7 @@ class AccessController < ApplicationController # GET maps/:id/deny_access/:request_id def deny_access request = AccessRequest.find(params[:request_id]) - request.deny + request.deny # also marks mailboxer notification as read respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was turned down' } end diff --git a/app/models/access_request.rb b/app/models/access_request.rb index 185a04f0..504e0ff2 100644 --- a/app/models/access_request.rb +++ b/app/models/access_request.rb @@ -6,13 +6,23 @@ class AccessRequest < ApplicationRecord self.approved = true self.answered = true self.save - UserMap.create(user: self.user, map: self.map) - MapMailer.invite_to_edit_email(self.map, self.map.user, self.user).deliver_later + + Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) + end + + UserMap.create(user: user, map: map) + mail = MapMailer.invite_to_edit_email(map, map.user, user) + user.notify(mail.subject, 'invite to edit', self, true, MAILBOXER_CODE_INVITED_TO_EDIT) end def deny self.approved = false self.answered = true self.save + + Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) + end end end From 88e98c734298456adc8cdd4e82f26abc79705a86 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Fri, 9 Dec 2016 11:55:53 -0500 Subject: [PATCH 08/16] polish mailboxer with bug fixes --- app/assets/stylesheets/clean.css.erb | 2 +- app/controllers/access_controller.rb | 5 +++-- app/models/access_request.rb | 4 ++-- .../new_notification_email.html.erb | 6 ++++-- .../new_notification_email.text.erb | 4 +++- config/environments/production.rb | 10 +++++----- config/initializers/mailboxer.rb | 2 +- 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index f4352504..fb55b29f 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -625,7 +625,7 @@ .sidebarForkIcon div:after, .sidebarFilterIcon div:after, .notificationsIcon div:after, -.sidebarAccountIcon .tooltipsUnder:after, +.sidebarAccountIcon .tooltipsUnder:after { content: ''; position: absolute; right: 40%; diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index d3808a1a..6f3518d5 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -36,8 +36,9 @@ class AccessController < ApplicationController @map.add_new_collaborators(user_ids).each do |user_id| # add_new_collaborators returns array of added users, # who we then send an email to - mail = MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)) - user.notify(mail.subject, 'invite to edit', UserMap.find_by(user_id: user_id, map: @map), true, MAILBOXER_CODE_INVITED_TO_EDIT) + user = User.find(user_id) + mail = MapMailer.invite_to_edit_email(@map, current_user, user) + user.notify(mail.subject, 'invite to edit', UserMap.find_by(user: user, map: @map), true, MAILBOXER_CODE_INVITED_TO_EDIT) end @map.remove_old_collaborators(user_ids) diff --git a/app/models/access_request.rb b/app/models/access_request.rb index 504e0ff2..d062ab79 100644 --- a/app/models/access_request.rb +++ b/app/models/access_request.rb @@ -11,9 +11,9 @@ class AccessRequest < ApplicationRecord Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) end - UserMap.create(user: user, map: map) + user_map = UserMap.create(user: user, map: map) mail = MapMailer.invite_to_edit_email(map, map.user, user) - user.notify(mail.subject, 'invite to edit', self, true, MAILBOXER_CODE_INVITED_TO_EDIT) + user.notify(mail.subject, 'invite to edit', user_map, true, MAILBOXER_CODE_INVITED_TO_EDIT) end def deny diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb index d8fda23c..87d8da33 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -1,4 +1,6 @@ <% mail = ApplicationMailer.mail_for_notification(@notification) %> -<% @notification.update(body: mail.html_part&.body&.decoded) %> -<%= raw mail.html_part&.body&.decoded %> +<% if mail %> + <% @notification.update(body: mail.html_part&.body&.decoded) %> + <%= raw mail.html_part&.body&.decoded %> +<% end %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb index 45fa8ae0..58623b16 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -1,2 +1,4 @@ <% mail = ApplicationMailer.mail_for_notification(@notification) %> -<%= mail.text_part&.body&.decoded %> +<% if mail %> + <%= mail.text_part&.body&.decoded %> +<% end %> diff --git a/config/environments/production.rb b/config/environments/production.rb index d3f8794e..ab4769b6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,16 +1,16 @@ + # frozen_string_literal: true Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb - config.log_level = :warn - config.eager_load = true - - # 12 factor: log to stdout - logger = ActiveSupport::Logger.new(STDOUT) + # log to stdout + logger = Logger.new(STDOUT) logger.formatter = config.log_formatter + logger.level = :warn config.logger = ActiveSupport::TaggedLogging.new(logger) # Code is not reloaded between requests + config.eager_load = true config.cache_classes = true # Full error reports are disabled and caching is turned on diff --git a/config/initializers/mailboxer.rb b/config/initializers/mailboxer.rb index 115d80a4..b937df92 100644 --- a/config/initializers/mailboxer.rb +++ b/config/initializers/mailboxer.rb @@ -15,7 +15,7 @@ Mailboxer.setup do |config| config.uses_emails = true # Configures the default from for emails sent for Messages and Notifications - config.default_from = 'no-reply@metamaps.cc' + config.default_from = 'team@metamaps.cc' # Configures the methods needed by mailboxer config.email_method = :mailboxer_email From 0960159265ee6197d09f1173ddccc69267c6927f Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sun, 11 Dec 2016 17:29:48 -0500 Subject: [PATCH 09/16] Mailboxer notification pagination --- app/controllers/access_controller.rb | 4 +++- app/controllers/notifications_controller.rb | 2 +- app/controllers/users_controller.rb | 4 +++- app/helpers/application_helper.rb | 2 +- app/models/access_request.rb | 6 +++--- app/models/user_map.rb | 2 +- app/views/notifications/index.html.erb | 7 +++++++ frontend/src/Metamaps/GlobalUI/NotificationIcon.js | 6 +++--- frontend/src/components/NotificationIcon.js | 13 ++++++------- 9 files changed, 28 insertions(+), 18 deletions(-) diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index 6f3518d5..5b6dea6e 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -38,7 +38,9 @@ class AccessController < ApplicationController # who we then send an email to user = User.find(user_id) mail = MapMailer.invite_to_edit_email(@map, current_user, user) - user.notify(mail.subject, 'invite to edit', UserMap.find_by(user: user, map: @map), true, MAILBOXER_CODE_INVITED_TO_EDIT) + user.notify(mail.subject, 'invite to edit', + UserMap.find_by(user: user, map: @map), + true, MAILBOXER_CODE_INVITED_TO_EDIT) end @map.remove_old_collaborators(user_ids) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 4759ef20..16049fc0 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -5,7 +5,7 @@ class NotificationsController < ApplicationController before_action :set_receipt, only: [:show, :mark_read, :mark_unread] def index - @notifications = current_user.mailbox.notifications + @notifications = current_user.mailbox.notifications.page(params[:page]).per(25) respond_to do |format| format.html diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6b771d6f..1defb323 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -95,6 +95,8 @@ class UsersController < ApplicationController private def user_params - params.require(:user).permit(:name, :email, :image, :password, :password_confirmation, :emails_allowed) + params.require(:user).permit( + :name, :email, :image, :password, :password_confirmation, :emails_allowed + ) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 45a5d565..96b5a2b2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,7 +40,7 @@ module ApplicationHelper def user_unread_notification_count return 0 if current_user.nil? - @user_unread_notification_count ||= current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| + @uunc ||= current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| receipt.is_read ? total : total + 1 end end diff --git a/app/models/access_request.rb b/app/models/access_request.rb index d062ab79..c433f7cc 100644 --- a/app/models/access_request.rb +++ b/app/models/access_request.rb @@ -7,10 +7,10 @@ class AccessRequest < ApplicationRecord self.answered = true self.save - Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Notification.where(notified_object: self).find_each do |notification| Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) end - + user_map = UserMap.create(user: user, map: map) mail = MapMailer.invite_to_edit_email(map, map.user, user) user.notify(mail.subject, 'invite to edit', user_map, true, MAILBOXER_CODE_INVITED_TO_EDIT) @@ -21,7 +21,7 @@ class AccessRequest < ApplicationRecord self.answered = true self.save - Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Notification.where(notified_object: self).find_each do |notification| Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) end end diff --git a/app/models/user_map.rb b/app/models/user_map.rb index 3aa87b03..c1cdc58e 100644 --- a/app/models/user_map.rb +++ b/app/models/user_map.rb @@ -4,7 +4,7 @@ class UserMap < ApplicationRecord belongs_to :user def mark_invite_notifications_as_read - Mailboxer::Notification.where(notified_object: self).each do |notification| + Mailboxer::Notification.where(notified_object: self).find_each do |notification| Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) end end diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index bd8022e5..e030e483 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -30,6 +30,13 @@ <% end %>
    + + <% if @notifications.total_pages > 1 %> + + <% end %> + <%= render partial: 'shared/back_to_mapping' %>
    diff --git a/frontend/src/Metamaps/GlobalUI/NotificationIcon.js b/frontend/src/Metamaps/GlobalUI/NotificationIcon.js index 2f13adba..1e9ff3bd 100644 --- a/frontend/src/Metamaps/GlobalUI/NotificationIcon.js +++ b/frontend/src/Metamaps/GlobalUI/NotificationIcon.js @@ -8,7 +8,7 @@ import NotificationIconComponent from '../../components/NotificationIcon' const NotificationIcon = { unreadNotificationsCount: null, - + init: function(serverData) { const self = NotificationIcon self.unreadNotificationsCount = serverData.unreadNotificationsCount @@ -18,7 +18,7 @@ const NotificationIcon = { if (newUnreadCount !== null) { NotificationIcon.unreadNotificationsCount = newUnreadCount } - + if (Active.Mapper !== null) { ReactDOM.render(React.createElement(NotificationIconComponent, { unreadNotificationsCount: NotificationIcon.unreadNotificationsCount @@ -27,4 +27,4 @@ const NotificationIcon = { } } -export default NotificationIcon \ No newline at end of file +export default NotificationIcon diff --git a/frontend/src/components/NotificationIcon.js b/frontend/src/components/NotificationIcon.js index 98782a75..5a1e820f 100644 --- a/frontend/src/components/NotificationIcon.js +++ b/frontend/src/components/NotificationIcon.js @@ -9,14 +9,14 @@ class NotificationIcon extends Component { } render = () => { - var linkClasses = "notificationsIcon upperRightEl upperRightIcon " - + let linkClasses = 'notificationsIcon upperRightEl upperRightIcon ' + if (this.props.unreadNotificationsCount > 0) { - linkClasses += "unread" + linkClasses += 'unread' } else { - linkClasses += "read" + linkClasses += 'read' } - + return (
    @@ -26,8 +26,7 @@ class NotificationIcon extends Component {
    )}
    - - + ) } } From 6d8392d2e715c9daab69615be9fb95edb677d8cd Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 12 Dec 2016 16:07:34 -0500 Subject: [PATCH 10/16] Make mailboxer look good and update email templates --- app/assets/stylesheets/apps.css.erb | 16 ++-- app/assets/stylesheets/clean.css.erb | 2 + app/assets/stylesheets/mobile.scss.erb | 6 +- app/assets/stylesheets/notifications.scss.erb | 76 +++++++++++++------ .../authorized_applications/index.html.erb | 1 + app/views/layouts/_upperelements.html.erb | 4 +- app/views/layouts/application.html.erb | 2 +- app/views/layouts/doorkeeper.html.erb | 3 +- .../new_notification_email.html.erb | 12 ++- .../new_notification_email.text.erb | 4 + .../map_mailer/access_request_email.html.erb | 22 ++---- .../map_mailer/access_request_email.text.erb | 2 - .../map_mailer/invite_to_edit_email.html.erb | 22 ++---- .../map_mailer/invite_to_edit_email.text.erb | 4 - app/views/notifications/index.html.erb | 7 +- app/views/notifications/show.html.erb | 4 +- app/views/shared/_back_to_mapping.html.erb | 2 +- frontend/src/components/NotificationIcon.js | 2 +- 18 files changed, 102 insertions(+), 89 deletions(-) diff --git a/app/assets/stylesheets/apps.css.erb b/app/assets/stylesheets/apps.css.erb index f3f444c7..46fa64b7 100644 --- a/app/assets/stylesheets/apps.css.erb +++ b/app/assets/stylesheets/apps.css.erb @@ -1,8 +1,8 @@ .centerContent { position: relative; - margin: 92px auto 0 auto; - padding: 20px 0 60px 20px; - width: 760px; + margin: 0 auto; + width: auto; + max-width: 960px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.24); background: #fff; @@ -10,7 +10,7 @@ -moz-border-radius: 3px; border-radius: 3px; border: 1px solid #dcdcdc; - margin-bottom: 10px; + box-sizing: border-box; padding: 15px; } @@ -130,10 +130,8 @@ border-radius: 2px; } -.back-to-mapping { - margin: 1em auto; - width: auto; - max-width: 960px; - box-sizing: border-box; +.centerContent.withPadding { + margin-top: 1em; + margin-bottom: 1em; } diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index fb55b29f..3970f877 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -28,6 +28,8 @@ position: absolute; width: 100%; height: 100%; + box-sizing: border-box; + padding-top: 92px; } /*.animations { diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index e7eb9a7d..8cdb3ae6 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -2,7 +2,7 @@ display: none; } -@media only screen and (max-width : 720px) and (min-width : 504px) { +@media only screen and (max-width : 752px) and (min-width : 504px) { .sidebarSearch .tt-hint, .sidebarSearch .sidebarSearchField { width: 160px !important; } @@ -57,7 +57,7 @@ } #yield { - height: 100%; + padding-top: 50px; } .new_session, .new_user, .edit_user, .login, .forgotPassword { @@ -66,7 +66,7 @@ left: auto; width: 78%; padding: 16px 10%; - margin: 50px auto 0 auto; + margin: 0 auto; } .centerGreyForm input[type="text"], .centerGreyForm input[type="email"], .centerGreyForm input[type="password"] { diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index d7942135..d9602804 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -20,16 +20,14 @@ $unread_notifications_dot_size: 8px; list-style: none; } - $menu_bar_height: 6em; .notificationPage, .notificationsPage { - width: auto; - max-width: 960px; - box-sizing: border-box; - margin: 1em auto; - margin-top: 1em + $menu_bar_height; font-family: 'din-regular', Sans-Serif; + & a:hover { + text-decoration: none; + } + & > .notification-title { border-bottom: 1px solid #eee; padding-bottom: 0.25em; @@ -41,34 +39,62 @@ $unread_notifications_dot_size: 8px; } } - - .notificationsPage .notification { - .notification-subject { - width: 25%; - } - .notification-body { - width: 50%; - } - .notification-read-unread { - width: 10%; + .notificationsPage { + header { + margin-bottom: 0; } - .notification-body, - .notification-subject, - .notification-read-unread { - display: inline-block; - vertical-align: top; + .notification:first-child { + border-top: none; + } + .notification:last-child { + border-bottom: 1px solid #DCDCDC; } - &.unread { - .notification-body, - .notification-subject, + .notification { + padding: 10px; + border:1px solid #DCDCDC; + border-bottom: none; + + &:hover { + background: #F6F6F6; + } + + & > a { + float: left; + width: 85%; + box-sizing: border-box; + padding-right: 10px; + } + .notification-read-unread { - font-family: 'din-medium', Sans-Serif; + float: left; + width: 15%; + } + + .notification-body, + .notification-subject { + display: inline-block; + vertical-align: top; + } + + .notification-body { + margin-left: 15px; + } + + &.unread { + .notification-body, + .notification-subject, + .notification-read-unread { + font-family: 'din-medium', Sans-Serif; + } } } + } + + .notificationPage .notification-body { p, div { margin: 1em auto; diff --git a/app/views/doorkeeper/authorized_applications/index.html.erb b/app/views/doorkeeper/authorized_applications/index.html.erb index 42c3127d..948efa30 100644 --- a/app/views/doorkeeper/authorized_applications/index.html.erb +++ b/app/views/doorkeeper/authorized_applications/index.html.erb @@ -33,5 +33,6 @@ <% end %>
    +<%= render partial: 'shared/back_to_mapping' %>
    <%= render 'script' %> diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index 1f499615..7ef76bad 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -4,7 +4,7 @@
    @@ -78,7 +78,7 @@ <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_unread_notification_count > 0 ? 'unread' : 'read'}" do %>
    - Notifications (<%= user_unread_notification_count %> unread) + Notifications
    <% if user_unread_notification_count > 0 %>
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d48ec0a1..2dd2a463 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -30,7 +30,7 @@
    - <%= render :partial => 'layouts/upperelements', :locals => { :appsPage => false } %> + <%= render :partial => 'layouts/upperelements', :locals => { :noHardHomeLink => controller_name == "notifications" ? true : false } %> <%= yield %> diff --git a/app/views/layouts/doorkeeper.html.erb b/app/views/layouts/doorkeeper.html.erb index a6a74f41..960502d9 100644 --- a/app/views/layouts/doorkeeper.html.erb +++ b/app/views/layouts/doorkeeper.html.erb @@ -22,7 +22,7 @@
    - <%= render :partial => 'layouts/upperelements', :locals => {:appsPage => true } %> + <%= render :partial => 'layouts/upperelements', :locals => {:noHardHomeLink => true } %> <%= yield %> @@ -50,7 +50,6 @@ <% end %>

    - <%= render partial: 'shared/back_to_mapping' %>
    <% end %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb index 87d8da33..7f680869 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -1,6 +1,10 @@ <% mail = ApplicationMailer.mail_for_notification(@notification) %> -<% if mail %> - <% @notification.update(body: mail.html_part&.body&.decoded) %> - <%= raw mail.html_part&.body&.decoded %> -<% end %> +
    + <% if mail %> + <% @notification.update(body: mail.html_part&.body&.decoded) %> + <%= raw mail.html_part&.body&.decoded %> + <% end %> +

    Make sense with Metamaps

    + <%= render partial: 'shared/mailer_unsubscribe_link' %> +
    diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb index 58623b16..a98af833 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -2,3 +2,7 @@ <% if mail %> <%= mail.text_part&.body&.decoded %> <% end %> + +Make sense with Metamaps + +<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/map_mailer/access_request_email.html.erb b/app/views/map_mailer/access_request_email.html.erb index 2e89eb6e..74d666bd 100644 --- a/app/views/map_mailer/access_request_email.html.erb +++ b/app/views/map_mailer/access_request_email.html.erb @@ -1,16 +1,6 @@ -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> - -

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    - -

    <%= @map.name %>

    - -

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> -

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    - - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - -

    Make sense with Metamaps

    - - <%= render partial: 'shared/mailer_unsubscribe_link' %> -
    +<% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    +

    <%= @map.name %>

    +

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> +

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    +<%= link_to 'Go to Map', map_url(@map), style: button_style %> diff --git a/app/views/map_mailer/access_request_email.text.erb b/app/views/map_mailer/access_request_email.text.erb index 25ccb2bd..ef302a8b 100644 --- a/app/views/map_mailer/access_request_email.text.erb +++ b/app/views/map_mailer/access_request_email.text.erb @@ -5,6 +5,4 @@ Allow [<%= approve_access_map_url(id: @map.id, request_id: @request.id) %>] Decline [<%= deny_access_map_url(id: @map.id, request_id: @request.id) %>] -Make sense with Metamaps -<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/map_mailer/invite_to_edit_email.html.erb b/app/views/map_mailer/invite_to_edit_email.html.erb index cd2b7b2e..aba7cfd4 100644 --- a/app/views/map_mailer/invite_to_edit_email.html.erb +++ b/app/views/map_mailer/invite_to_edit_email.html.erb @@ -1,15 +1,7 @@ -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> - -

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    -

    <%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    - <% if @map.desc %> -

    <%= @map.desc %>

    - <% end %> - - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - -

    Make sense with Metamaps

    - - <%= render partial: 'shared/mailer_unsubscribe_link' %> -
    +<% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    +

    <%= link_to @map.name, map_url(@map), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    +<% if @map.desc %> +

    <%= @map.desc %>

    +<% end %> +<%= link_to 'Go to Map', map_url(@map), style: button_style %> diff --git a/app/views/map_mailer/invite_to_edit_email.text.erb b/app/views/map_mailer/invite_to_edit_email.text.erb index 7d3bf397..4e822842 100644 --- a/app/views/map_mailer/invite_to_edit_email.text.erb +++ b/app/views/map_mailer/invite_to_edit_email.text.erb @@ -1,7 +1,3 @@ <%= @inviter.name %> has invited you to collaboratively edit the following map: <%= @map.name %> [<%= map_url(@map) %>] - -Make sense with Metamaps - -<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index e030e483..f2c98adf 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -3,7 +3,9 @@
    -

    My Notifications

    +
      <% @notifications.each do |notification| %> <% receipt = @receipts.find_by(notification_id: notification.id) %> @@ -23,6 +25,7 @@ <%= link_to 'mark as read', mark_read_notification_path(notification.id), remote: true, method: :put %> <% end %>
    +
    <% end %> <% if @notifications.count == 0 %> @@ -32,7 +35,7 @@
    <% if @notifications.total_pages > 1 %> -
    -
    - <%= link_to 'Back', notifications_path %> +
    + <%= link_to 'Back to notifications', notifications_path %>
    <%= render partial: 'shared/back_to_mapping' %> diff --git a/app/views/shared/_back_to_mapping.html.erb b/app/views/shared/_back_to_mapping.html.erb index 682a71e6..342fd186 100644 --- a/app/views/shared/_back_to_mapping.html.erb +++ b/app/views/shared/_back_to_mapping.html.erb @@ -1,3 +1,3 @@ -
    + diff --git a/frontend/src/components/NotificationIcon.js b/frontend/src/components/NotificationIcon.js index 5a1e820f..e7225f04 100644 --- a/frontend/src/components/NotificationIcon.js +++ b/frontend/src/components/NotificationIcon.js @@ -20,7 +20,7 @@ class NotificationIcon extends Component { return (
    - Notifications ({this.props.unreadNotificationsCount} unread) + Notifications
    {this.props.unreadNotificationsCount === 0 ? null : (
    From 87228c27c1a286789a5ef8f8a191fa6346307439 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 13 Dec 2016 02:42:33 -0500 Subject: [PATCH 11/16] Fix mailboxer + email bugs --- app/assets/stylesheets/mobile.scss.erb | 3 ++- app/assets/stylesheets/notifications.scss.erb | 4 +++ app/controllers/access_controller.rb | 11 +++----- app/mailers/application_mailer.rb | 7 +++-- app/mailers/map_mailer.rb | 16 +++++++----- app/models/access_request.rb | 11 ++++++-- app/models/map.rb | 4 +++ app/services/notification_service.rb | 26 +++++++++++++++++++ .../authorized_applications/index.html.erb | 2 +- .../new_notification_email.html.erb | 6 +---- .../map_mailer/access_approved_email.html.erb | 8 ++++++ .../map_mailer/access_approved_email.text.erb | 4 +++ .../map_mailer/access_request_email.html.erb | 12 +++++---- .../map_mailer/access_request_email.text.erb | 10 ++++--- .../map_mailer/invite_to_edit_email.html.erb | 12 +++++---- .../map_mailer/invite_to_edit_email.text.erb | 6 +++-- app/views/notifications/index.html.erb | 6 +++-- app/views/notifications/show.html.erb | 2 +- app/views/shared/_back_to_mapping.html.erb | 3 --- app/views/shared/_go_to_maps.html.erb | 3 +++ .../shared/_mailer_unsubscribe_link.html.erb | 2 +- .../shared/_mailer_unsubscribe_link.text.erb | 2 +- config/initializers/mailboxer.rb | 3 ++- frontend/src/Metamaps/Map/InfoBox.js | 2 +- spec/mailers/previews/map_mailer_preview.rb | 7 ++++- 25 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 app/services/notification_service.rb create mode 100644 app/views/map_mailer/access_approved_email.html.erb create mode 100644 app/views/map_mailer/access_approved_email.text.erb delete mode 100644 app/views/shared/_back_to_mapping.html.erb create mode 100644 app/views/shared/_go_to_maps.html.erb diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 8cdb3ae6..8deca0aa 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -240,8 +240,9 @@ position: relative; .unread-notifications-dot { - top: 17px; + top: 50%; left: 0px; + margin-top: -4px; } } } diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index d9602804..5058bc62 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -44,6 +44,10 @@ $unread_notifications_dot_size: 8px; margin-bottom: 0; } + .emptyInbox { + padding-top: 15px; + } + .notification:first-child { border-top: none; } diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index 5b6dea6e..a83fd128 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -20,9 +20,7 @@ class AccessController < ApplicationController # POST maps/:id/access_request def access_request request = AccessRequest.create(user: current_user, map: @map) - # what about push notification to map owner? - mail = MapMailer.access_request_email(request, @map) - @map.user.notify(mail.subject, 'access request', request, true, MAILBOXER_CODE_ACCESS_REQUEST) + NotificationService.access_request(request) respond_to do |format| format.json { head :ok } @@ -35,12 +33,9 @@ class AccessController < ApplicationController @map.add_new_collaborators(user_ids).each do |user_id| # add_new_collaborators returns array of added users, - # who we then send an email to + # who we then send a notification to user = User.find(user_id) - mail = MapMailer.invite_to_edit_email(@map, current_user, user) - user.notify(mail.subject, 'invite to edit', - UserMap.find_by(user: user, map: @map), - true, MAILBOXER_CODE_INVITED_TO_EDIT) + NotificationService.invite_to_edit(@map, current_user, user) end @map.remove_old_collaborators(user_ids) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 338f38ee..ebffb2df 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -11,8 +11,11 @@ class ApplicationMailer < ActionMailer::Base def mail_for_notification(notification) if notification.notification_code == MAILBOXER_CODE_ACCESS_REQUEST request = notification.notified_object - MapMailer.access_request_email(request, request.map) - elsif notification.notification_code == MAILBOXER_CODE_INVITED_TO_EDIT + MapMailer.access_request_email(request) + elsif notification.notification_code == MAILBOXER_CODE_ACCESS_APPROVED + request = notification.notified_object + MapMailer.access_approved_email(request) + elsif notification.notification_code == MAILBOXER_CODE_INVITE_TO_EDIT user_map = notification.notified_object MapMailer.invite_to_edit_email(user_map.map, user_map.map.user, user_map.user) end diff --git a/app/mailers/map_mailer.rb b/app/mailers/map_mailer.rb index f6865ecd..bf0cec7b 100644 --- a/app/mailers/map_mailer.rb +++ b/app/mailers/map_mailer.rb @@ -2,17 +2,21 @@ class MapMailer < ApplicationMailer default from: 'team@metamaps.cc' - def access_request_email(request, map) + def access_request_email(request) @request = request - @map = map - subject = @map.name + ' - request to edit' - mail(to: @map.user.email, subject: subject) + @map = request.map + mail(to: @map.user.email, subject: request.requested_text) + end + + def access_approved_email(request) + @request = request + @map = request.map + mail(to: request.user, subject: request.approved_text) end def invite_to_edit_email(map, inviter, invitee) @inviter = inviter @map = map - subject = @map.name + ' - invitation to edit' - mail(to: invitee.email, subject: subject) + mail(to: invitee.email, subject: map.invited_text) end end diff --git a/app/models/access_request.rb b/app/models/access_request.rb index c433f7cc..e5416fff 100644 --- a/app/models/access_request.rb +++ b/app/models/access_request.rb @@ -12,8 +12,7 @@ class AccessRequest < ApplicationRecord end user_map = UserMap.create(user: user, map: map) - mail = MapMailer.invite_to_edit_email(map, map.user, user) - user.notify(mail.subject, 'invite to edit', user_map, true, MAILBOXER_CODE_INVITED_TO_EDIT) + NotificationService.access_approved(self) end def deny @@ -25,4 +24,12 @@ class AccessRequest < ApplicationRecord Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) end end + + def requested_text + self.map.name + ' - request to edit' + end + + def approved_text + self.map.name + ' - access approved' + end end diff --git a/app/models/map.rb b/app/models/map.rb index 36b2d284..5744b856 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -123,4 +123,8 @@ class Map < ApplicationRecord Topic.where(defer_to_map_id: id).update_all(permission: permission) Synapse.where(defer_to_map_id: id).update_all(permission: permission) end + + def invited_text + self.name + ' - invited to edit' + end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb new file mode 100644 index 00000000..05a268f4 --- /dev/null +++ b/app/services/notification_service.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +class NotificationService + + def self.renderer + renderer ||= ApplicationController.renderer.new( + http_host: ENV['MAILER_DEFAULT_URL'], + https: Rails.env.production? ? true : false + ) + end + + def self.access_request(request) + body = renderer.render(template: 'map_mailer/access_request_email', locals: { map: request.map, request: request }, layout: false) + request.map.user.notify(request.requested_text, body, request, false, MAILBOXER_CODE_ACCESS_REQUEST) + end + + def self.access_approved(request) + body = renderer.render(template: 'map_mailer/access_approved_email', locals: { map: request.map }, layout: false) + receipt = request.user.notify(request.approved_text, body, request, false, MAILBOXER_CODE_ACCESS_APPROVED) + end + + def self.invite_to_edit(map, inviter, invited) + user_map = UserMap.find_by(user: invited, map: map) + body = renderer.render(template: 'map_mailer/invite_to_edit_email', locals: { map: map, inviter: inviter }, layout: false) + invited.notify(map.invited_text, body, user_map, false, MAILBOXER_CODE_INVITE_TO_EDIT) + end +end diff --git a/app/views/doorkeeper/authorized_applications/index.html.erb b/app/views/doorkeeper/authorized_applications/index.html.erb index 948efa30..d6391215 100644 --- a/app/views/doorkeeper/authorized_applications/index.html.erb +++ b/app/views/doorkeeper/authorized_applications/index.html.erb @@ -33,6 +33,6 @@ <% end %>
    -<%= render partial: 'shared/back_to_mapping' %> +<%= render partial: 'shared/go_to_maps' %>
    <%= render 'script' %> diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb index 7f680869..ad90419f 100644 --- a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -1,10 +1,6 @@ -<% mail = ApplicationMailer.mail_for_notification(@notification) %>
    - <% if mail %> - <% @notification.update(body: mail.html_part&.body&.decoded) %> - <%= raw mail.html_part&.body&.decoded %> - <% end %> + <%= raw @notification.body %>

    Make sense with Metamaps

    <%= render partial: 'shared/mailer_unsubscribe_link' %>
    diff --git a/app/views/map_mailer/access_approved_email.html.erb b/app/views/map_mailer/access_approved_email.html.erb new file mode 100644 index 00000000..91fd77e0 --- /dev/null +++ b/app/views/map_mailer/access_approved_email.html.erb @@ -0,0 +1,8 @@ +<% map = @map || map %> +<% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= map.user.name %> has responded to your access request and invited you to collaboratively edit the following map:

    +

    <%= link_to map.name, map_url(map), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    +<% if map.desc %> +

    <%= map.desc %>

    +<% end %> +<%= link_to 'Go to Map', map_url(map), style: button_style %> diff --git a/app/views/map_mailer/access_approved_email.text.erb b/app/views/map_mailer/access_approved_email.text.erb new file mode 100644 index 00000000..2a8e54f6 --- /dev/null +++ b/app/views/map_mailer/access_approved_email.text.erb @@ -0,0 +1,4 @@ +<% map = @map || map %> +<%= map.user.name %> has responded to your access request and invited you to collaboratively edit the following map: + +<%= map.name %> [<%= map_url(map) %>] diff --git a/app/views/map_mailer/access_request_email.html.erb b/app/views/map_mailer/access_request_email.html.erb index 74d666bd..759b97eb 100644 --- a/app/views/map_mailer/access_request_email.html.erb +++ b/app/views/map_mailer/access_request_email.html.erb @@ -1,6 +1,8 @@ +<% map = @map || map %> +<% request = @request || request %> <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    -

    <%= @map.name %>

    -

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> -

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    -<%= link_to 'Go to Map', map_url(@map), style: button_style %> +

    <%= request.user.name %> is requesting access to collaboratively edit the following map:

    +

    <%= map.name %>

    +

    <%= link_to "Allow", approve_access_map_url(id: map.id, request_id: request.id), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> +

    <%= link_to "Decline", deny_access_map_url(id: map.id, request_id: request.id), style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    +<%= link_to 'Go to Map', map_url(map), style: button_style %> diff --git a/app/views/map_mailer/access_request_email.text.erb b/app/views/map_mailer/access_request_email.text.erb index ef302a8b..c99aa6e6 100644 --- a/app/views/map_mailer/access_request_email.text.erb +++ b/app/views/map_mailer/access_request_email.text.erb @@ -1,8 +1,10 @@ -<%= @request.user.name %> has requested to collaboratively edit the following map: +<% map = @map || map %> +<% request = @request || request %> +<%= request.user.name %> has requested to collaboratively edit the following map: -<%= @map.name %> [<%= map_url(@map) %>] +<%= map.name %> [<%= map_url(map) %>] -Allow [<%= approve_access_map_url(id: @map.id, request_id: @request.id) %>] -Decline [<%= deny_access_map_url(id: @map.id, request_id: @request.id) %>] +Allow [<%= approve_access_map_url(id: map.id, request_id: request.id) %>] +Decline [<%= deny_access_map_url(id: map.id, request_id: request.id) %>] diff --git a/app/views/map_mailer/invite_to_edit_email.html.erb b/app/views/map_mailer/invite_to_edit_email.html.erb index aba7cfd4..f08cc377 100644 --- a/app/views/map_mailer/invite_to_edit_email.html.erb +++ b/app/views/map_mailer/invite_to_edit_email.html.erb @@ -1,7 +1,9 @@ +<% map = @map || map %> +<% inviter = @inviter || inviter %> <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    -

    <%= link_to @map.name, map_url(@map), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    -<% if @map.desc %> -

    <%= @map.desc %>

    +

    <%= inviter.name %> has invited you to collaboratively edit the following map:

    +

    <%= link_to map.name, map_url(map), style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    +<% if map.desc %> +

    <%= map.desc %>

    <% end %> -<%= link_to 'Go to Map', map_url(@map), style: button_style %> +<%= link_to 'Go to Map', map_url(map), style: button_style %> diff --git a/app/views/map_mailer/invite_to_edit_email.text.erb b/app/views/map_mailer/invite_to_edit_email.text.erb index 4e822842..b58cced9 100644 --- a/app/views/map_mailer/invite_to_edit_email.text.erb +++ b/app/views/map_mailer/invite_to_edit_email.text.erb @@ -1,3 +1,5 @@ -<%= @inviter.name %> has invited you to collaboratively edit the following map: +<% map = @map || map %> +<% inviter = @inviter || inviter %> +<%= inviter.name %> has invited you to collaboratively edit the following map: -<%= @map.name %> [<%= map_url(@map) %>] +<%= map.name %> [<%= map_url(map) %>] diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index f2c98adf..9e37c220 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -29,7 +29,9 @@ <% end %> <% if @notifications.count == 0 %> - You have ZERO unread notifications. Huzzah! +
    + You have ZERO unread notifications. Huzzah! +
    <% end %>
    @@ -40,7 +42,7 @@ <% end %> - <%= render partial: 'shared/back_to_mapping' %> + <%= render partial: 'shared/go_to_maps' %> <%= render partial: 'notifications/header' %> diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index 0af259ea..a003a0e1 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -13,7 +13,7 @@ <%= link_to 'Back to notifications', notifications_path %> - <%= render partial: 'shared/back_to_mapping' %> + <%= render partial: 'shared/go_to_maps' %> <%= render partial: 'notifications/header' %> diff --git a/app/views/shared/_back_to_mapping.html.erb b/app/views/shared/_back_to_mapping.html.erb deleted file mode 100644 index 342fd186..00000000 --- a/app/views/shared/_back_to_mapping.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/app/views/shared/_go_to_maps.html.erb b/app/views/shared/_go_to_maps.html.erb new file mode 100644 index 00000000..04c88574 --- /dev/null +++ b/app/views/shared/_go_to_maps.html.erb @@ -0,0 +1,3 @@ + diff --git a/app/views/shared/_mailer_unsubscribe_link.html.erb b/app/views/shared/_mailer_unsubscribe_link.html.erb index 2bbe7a89..56730dd9 100644 --- a/app/views/shared/_mailer_unsubscribe_link.html.erb +++ b/app/views/shared/_mailer_unsubscribe_link.html.erb @@ -1,3 +1,3 @@ diff --git a/app/views/shared/_mailer_unsubscribe_link.text.erb b/app/views/shared/_mailer_unsubscribe_link.text.erb index 3a2f7d0d..ff851865 100644 --- a/app/views/shared/_mailer_unsubscribe_link.text.erb +++ b/app/views/shared/_mailer_unsubscribe_link.text.erb @@ -2,4 +2,4 @@ You can unsubscribe from all Metamaps emails by visiting the following link: -<%= unsubscribe_notifications_url %> +<%= unsubscribe_notifications_url(protocol: Rails.env.production? ? :https : :http) %> diff --git a/config/initializers/mailboxer.rb b/config/initializers/mailboxer.rb index b937df92..49824f50 100644 --- a/config/initializers/mailboxer.rb +++ b/config/initializers/mailboxer.rb @@ -8,7 +8,8 @@ # }, # which would imply that this is an access request to Map.find(1) MAILBOXER_CODE_ACCESS_REQUEST = 'ACCESS_REQUEST' -MAILBOXER_CODE_INVITED_TO_EDIT = 'INVITED_TO_EDIT' +MAILBOXER_CODE_ACCESS_APPROVED = 'ACCESS_APPROVED' +MAILBOXER_CODE_INVITE_TO_EDIT = 'INVITE_TO_EDIT' Mailboxer.setup do |config| # Configures if your application uses or not email sending for Notifications and Messages diff --git a/frontend/src/Metamaps/Map/InfoBox.js b/frontend/src/Metamaps/Map/InfoBox.js index 1b06daf5..5fced292 100644 --- a/frontend/src/Metamaps/Map/InfoBox.js +++ b/frontend/src/Metamaps/Map/InfoBox.js @@ -264,7 +264,7 @@ const InfoBox = { var mapperIds = DataModel.Collaborators.models.map(function(mapper) { return mapper.id }) $.post('/maps/' + Active.Map.id + '/access', { access: mapperIds }) var name = DataModel.Collaborators.get(newCollaboratorId).get('name') - GlobalUI.notifyUser(name + ' will be notified by email') + GlobalUI.notifyUser(name + ' will be notified') self.updateNumbers() } diff --git a/spec/mailers/previews/map_mailer_preview.rb b/spec/mailers/previews/map_mailer_preview.rb index 17ea7671..61e33eb8 100644 --- a/spec/mailers/previews/map_mailer_preview.rb +++ b/spec/mailers/previews/map_mailer_preview.rb @@ -7,6 +7,11 @@ class MapMailerPreview < ActionMailer::Preview def access_request_email request = AccessRequest.first - MapMailer.access_request_email(request, request.map) + MapMailer.access_request_email(request) + end + + def access_approved_email + request = AccessRequest.first + MapMailer.access_approved_email(request) end end From 186129807e892ee09ea9c5de8686377c14517244 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Mon, 12 Dec 2016 22:28:10 -0500 Subject: [PATCH 12/16] fix spec, bugs, style --- app/controllers/access_controller.rb | 1 - app/controllers/application_controller.rb | 6 ++--- app/controllers/topics_controller.rb | 6 ++--- .../users/registrations_controller.rb | 5 +--- app/helpers/topics_helper.rb | 2 +- app/models/access_request.rb | 9 ++++--- app/models/map.rb | 12 ++++----- app/models/webhooks/slack/base.rb | 4 +-- app/policies/explore_policy.rb | 1 + app/policies/hack_policy.rb | 1 + app/policies/map_policy.rb | 2 +- app/policies/topic_policy.rb | 2 +- app/services/notification_service.rb | 1 - config/initializers/doorkeeper.rb | 8 +++--- config/initializers/rack-attack.rb | 25 +++++++++---------- config/locales/en.yml | 10 +++----- spec/api/v2/mappings_api_spec.rb | 1 - spec/api/v2/maps_api_spec.rb | 5 ++-- spec/api/v2/topics_api_spec.rb | 1 - spec/controllers/synapses_controller_spec.rb | 4 +-- spec/mailers/map_mailer_spec.rb | 5 ++-- spec/models/access_request_spec.rb | 7 +++--- 22 files changed, 54 insertions(+), 64 deletions(-) diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index a83fd128..2441aa62 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -6,7 +6,6 @@ class AccessController < ApplicationController :deny_access, :deny_access_post, :request_access] after_action :verify_authorized - # GET maps/:id/request_access def request_access @map = nil diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4285682e..4bb5be10 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,7 +22,7 @@ class ApplicationController < ActionController::Base helper_method :admin? def handle_unauthorized - if authenticated? and params[:controller] == 'maps' and params[:action] == 'show' + if authenticated? && (params[:controller] == 'maps') && (params[:action] == 'show') redirect_to request_access_map_path(params[:id]) elsif authenticated? redirect_to root_path, notice: "You don't have permission to see that page." @@ -41,13 +41,13 @@ class ApplicationController < ActionController::Base def require_no_user return true unless authenticated? redirect_to edit_user_path(user), notice: 'You must be logged out.' - return false + false end def require_user return true if authenticated? redirect_to sign_in_path, notice: 'You must be logged in.' - return false + false end def require_admin diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index ea56059b..b54cc4f5 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -14,14 +14,14 @@ class TopicsController < ApplicationController @topics = policy_scope(Topic).where('LOWER("name") like ?', term.downcase + '%').order('"name"') @mapTopics = @topics.select { |t| t&.metacode&.name == 'Metamap' } # prioritize topics which point to maps, over maps - @exclude = @mapTopics.length > 0 ? @mapTopics.map(&:name) : [''] + @exclude = @mapTopics.length.positive? ? @mapTopics.map(&:name) : [''] @maps = policy_scope(Map).where('LOWER("name") like ? AND name NOT IN (?)', term.downcase + '%', @exclude).order('"name"') else @topics = [] @maps = [] end - @all= @topics.to_a.concat(@maps.to_a).sort { |a, b| a.name <=> b.name } - + @all = @topics.to_a.concat(@maps.to_a).sort_by(&:name) + render json: autocomplete_array_json(@all).to_json end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 7c211f26..44bcb2de 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -21,13 +21,10 @@ class Users::RegistrationsController < Devise::RegistrationsController end end - private def store_location - if params[:redirect_to] - store_location_for(User, params[:redirect_to]) - end + store_location_for(User, params[:redirect_to]) if params[:redirect_to] end def configure_sign_up_params diff --git a/app/helpers/topics_helper.rb b/app/helpers/topics_helper.rb index 58f53a6e..fa26095d 100644 --- a/app/helpers/topics_helper.rb +++ b/app/helpers/topics_helper.rb @@ -20,7 +20,7 @@ module TopicsHelper type: is_map ? metamapMetacode.name : t.metacode.name, typeImageURL: is_map ? metamapMetacode.icon : t.metacode.icon, mapCount: is_map ? 0 : t.maps.count, - synapseCount: is_map ? 0 : t.synapses.count, + synapseCount: is_map ? 0 : t.synapses.count } end end diff --git a/app/models/access_request.rb b/app/models/access_request.rb index e5416fff..fe68ce8f 100644 --- a/app/models/access_request.rb +++ b/app/models/access_request.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class AccessRequest < ApplicationRecord belongs_to :user belongs_to :map @@ -5,7 +6,7 @@ class AccessRequest < ApplicationRecord def approve self.approved = true self.answered = true - self.save + save Mailboxer::Notification.where(notified_object: self).find_each do |notification| Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) @@ -18,7 +19,7 @@ class AccessRequest < ApplicationRecord def deny self.approved = false self.answered = true - self.save + save Mailboxer::Notification.where(notified_object: self).find_each do |notification| Mailboxer::Receipt.where(notification: notification).update_all(is_read: true) @@ -26,10 +27,10 @@ class AccessRequest < ApplicationRecord end def requested_text - self.map.name + ' - request to edit' + map.name + ' - request to edit' end def approved_text - self.map.name + ' - access approved' + map.name + ' - access approved' end end diff --git a/app/models/map.rb b/app/models/map.rb index 5744b856..899992c6 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -18,11 +18,11 @@ class Map < ApplicationRecord # This method associates the attribute ":image" with a file attachment has_attached_file :screenshot, - styles: { - thumb: ['220x220#', :png] - #:full => ['940x630#', :png] - }, - default_url: 'https://s3.amazonaws.com/metamaps-assets/site/missing-map-square.png' + styles: { + thumb: ['220x220#', :png] + #:full => ['940x630#', :png] + }, + default_url: 'https://s3.amazonaws.com/metamaps-assets/site/missing-map-square.png' validates :name, presence: true validates :arranged, inclusion: { in: [true, false] } @@ -125,6 +125,6 @@ class Map < ApplicationRecord end def invited_text - self.name + ' - invited to edit' + name + ' - invited to edit' end end diff --git a/app/models/webhooks/slack/base.rb b/app/models/webhooks/slack/base.rb index 2274e32c..ba4e9ea5 100644 --- a/app/models/webhooks/slack/base.rb +++ b/app/models/webhooks/slack/base.rb @@ -14,9 +14,7 @@ Webhooks::Slack::Base = Struct.new(:webhook, :event) do 'something' end - def channel - webhook.channel - end + delegate :channel, to: :webhook def attachments [{ diff --git a/app/policies/explore_policy.rb b/app/policies/explore_policy.rb index b4d52fe5..ce17d4f4 100644 --- a/app/policies/explore_policy.rb +++ b/app/policies/explore_policy.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class ExplorePolicy < ApplicationPolicy def active? true diff --git a/app/policies/hack_policy.rb b/app/policies/hack_policy.rb index b6fbf6ce..bdc9eaab 100644 --- a/app/policies/hack_policy.rb +++ b/app/policies/hack_policy.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class HackPolicy < ApplicationPolicy def load_url_title? true diff --git a/app/policies/map_policy.rb b/app/policies/map_policy.rb index f670f59e..937d564c 100644 --- a/app/policies/map_policy.rb +++ b/app/policies/map_policy.rb @@ -16,7 +16,7 @@ class MapPolicy < ApplicationPolicy end def show? - record.permission.in?(['commons', 'public']) || + record.permission.in?(%w(commons public)) || record.collaborators.include?(user) || record.user == user end diff --git a/app/policies/topic_policy.rb b/app/policies/topic_policy.rb index 64463b4a..bc80f657 100644 --- a/app/policies/topic_policy.rb +++ b/app/policies/topic_policy.rb @@ -22,7 +22,7 @@ class TopicPolicy < ApplicationPolicy if record.defer_to_map.present? map_policy.show? else - record.permission.in?(['commons', 'public']) || record.user == user + record.permission.in?(%w(commons public)) || record.user == user end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 05a268f4..41202345 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true class NotificationService - def self.renderer renderer ||= ApplicationController.renderer.new( http_host: ENV['MAILER_DEFAULT_URL'], diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 433b1c40..21b08bc2 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -9,20 +9,20 @@ Doorkeeper.configure do current_user else store_location_for(User, request.fullpath) - redirect_to(sign_in_url, notice: "Sign In to Connect") + redirect_to(sign_in_url, notice: 'Sign In to Connect') end end # If you want to restrict access to the web interface for adding oauth authorized applications, # you need to declare the block below. admin_authenticator do - if current_user && current_user.admin + if current_user&.admin current_user elsif current_user && !current_user.admin - redirect_to(root_url, notice: "Unauthorized") + redirect_to(root_url, notice: 'Unauthorized') else store_location_for(User, request.fullpath) - redirect_to(sign_in_url, notice: "Try signing in to do that") + redirect_to(sign_in_url, notice: 'Try signing in to do that') end end diff --git a/config/initializers/rack-attack.rb b/config/initializers/rack-attack.rb index 1cb90f0f..0fd76889 100644 --- a/config/initializers/rack-attack.rb +++ b/config/initializers/rack-attack.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class Rack::Attack Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new @@ -11,10 +12,8 @@ class Rack::Attack # Throttle POST requests to /login by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}" - throttle('logins/ip', :limit => 5, :period => 20.seconds) do |req| - if req.path == '/login' && req.post? - req.ip - end + throttle('logins/ip', limit: 5, period: 20.seconds) do |req| + req.ip if req.path == '/login' && req.post? end # Throttle POST requests to /login by email param @@ -25,17 +24,17 @@ class Rack::Attack # throttle logins for another user and force their login requests to be # denied, but that's not very common and shouldn't happen to you. (Knock # on wood!) - throttle("logins/email", :limit => 5, :period => 20.seconds) do |req| + throttle('logins/email', limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? # return the email if present, nil otherwise req.params['email'].presence end end - throttle('load_url_title/req/5mins/ip', :limit => 300, :period => 5.minutes) do |req| + throttle('load_url_title/req/5mins/ip', limit: 300, period: 5.minutes) do |req| req.ip if req.path == 'hacks/load_url_title' end - throttle('load_url_title/req/1s/ip', :limit => 5, :period => 1.second) do |req| + throttle('load_url_title/req/1s/ip', limit: 5, period: 1.second) do |req| # If the return value is truthy, the cache key for the return value # is incremented and compared with the limit. In this case: # "rack::attack:#{Time.now.to_i/1.second}:load_url_title/req/ip:#{req.ip}" @@ -46,16 +45,16 @@ class Rack::Attack end self.throttled_response = lambda do |env| - now = Time.now - match_data = env['rack.attack.match_data'] + now = Time.now + match_data = env['rack.attack.match_data'] period = match_data[:period] limit = match_data[:limit] - headers = { + headers = { 'X-RateLimit-Limit' => limit.to_s, - 'X-RateLimit-Remaining' => '0', - 'X-RateLimit-Reset' => (now + (period - now.to_i % period)).to_s - } + 'X-RateLimit-Remaining' => '0', + 'X-RateLimit-Reset' => (now + (period - now.to_i % period)).to_s + } [429, headers, ['']] end diff --git a/config/locales/en.yml b/config/locales/en.yml index 46d3db07..c4ada107 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,8 +1,4 @@ -# Sample localization file for English. Add more files in this directory for other locales. -# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. - en: - activerecord: - attributes: - user: - joinedwithcode: "Access code" + mailboxer: + notification_mailer: + subject: "%{subject}" diff --git a/spec/api/v2/mappings_api_spec.rb b/spec/api/v2/mappings_api_spec.rb index 6f225c6a..f8854e91 100644 --- a/spec/api/v2/mappings_api_spec.rb +++ b/spec/api/v2/mappings_api_spec.rb @@ -18,7 +18,6 @@ RSpec.describe 'mappings API', type: :request do it 'GET /api/v2/mappings/:id' do get "/api/v2/mappings/#{mapping.id}", params: { access_token: token } - expect(response).to have_http_status(:success) expect(response).to match_json_schema(:mapping) expect(JSON.parse(response.body)['data']['id']).to eq mapping.id diff --git a/spec/api/v2/maps_api_spec.rb b/spec/api/v2/maps_api_spec.rb index a7edeef2..fbf07903 100644 --- a/spec/api/v2/maps_api_spec.rb +++ b/spec/api/v2/maps_api_spec.rb @@ -1,4 +1,5 @@ -#t frozen_string_literal: true +# frozen_string_literal: true +# t frozen_string_literal: true require 'rails_helper' RSpec.describe 'maps API', type: :request do @@ -35,7 +36,7 @@ RSpec.describe 'maps API', type: :request do expect(response).to match_json_schema(:map) expect(JSON.parse(response.body)['data']['id']).to eq map.id end - + it 'POST /api/v2/maps' do post '/api/v2/maps', params: { map: map.attributes, access_token: token } diff --git a/spec/api/v2/topics_api_spec.rb b/spec/api/v2/topics_api_spec.rb index 31d93b87..ac2fb56a 100644 --- a/spec/api/v2/topics_api_spec.rb +++ b/spec/api/v2/topics_api_spec.rb @@ -18,7 +18,6 @@ RSpec.describe 'topics API', type: :request do it 'GET /api/v2/topics/:id' do get "/api/v2/topics/#{topic.id}" - expect(response).to have_http_status(:success) expect(response).to match_json_schema(:topic) expect(JSON.parse(response.body)['data']['id']).to eq topic.id diff --git a/spec/controllers/synapses_controller_spec.rb b/spec/controllers/synapses_controller_spec.rb index 511971ad..7abeb2ee 100644 --- a/spec/controllers/synapses_controller_spec.rb +++ b/spec/controllers/synapses_controller_spec.rb @@ -53,9 +53,9 @@ RSpec.describe SynapsesController, type: :controller do expect(response.status).to eq 422 end it 'does not create a synapse' do - expect { + expect do post :create, format: :json, params: { synapse: invalid_attributes } - }.to change { + end.to change { Synapse.count }.by 0 end diff --git a/spec/mailers/map_mailer_spec.rb b/spec/mailers/map_mailer_spec.rb index 5fed48f5..a920746b 100644 --- a/spec/mailers/map_mailer_spec.rb +++ b/spec/mailers/map_mailer_spec.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true require 'rails_helper' RSpec.describe MapMailer, type: :mailer do describe 'access_request_email' do - let(:request) { create(:access_request) } let(:map) { create(:map) } - let(:mail) { described_class.access_request_email(request, map) } + let(:request) { create(:access_request, map: map) } + let(:mail) { described_class.access_request_email(request) } it { expect(mail.from).to eq ['team@metamaps.cc'] } it { expect(mail.to).to eq [map.user.email] } diff --git a/spec/models/access_request_spec.rb b/spec/models/access_request_spec.rb index 98490bf7..e8db280b 100644 --- a/spec/models/access_request_spec.rb +++ b/spec/models/access_request_spec.rb @@ -1,8 +1,7 @@ +# frozen_string_literal: true require 'rails_helper' RSpec.describe AccessRequest, type: :model do - include ActiveJob::TestHelper # enqueued_jobs - let(:access_request) { create(:access_request) } describe 'approve' do @@ -13,7 +12,7 @@ RSpec.describe AccessRequest, type: :model do it { expect(access_request.approved).to be true } it { expect(access_request.answered).to be true } it { expect(UserMap.count).to eq 1 } - it { expect(enqueued_jobs.count).to eq 1 } + it { expect(Mailboxer::Notification.count).to eq 1 } end describe 'deny' do @@ -24,6 +23,6 @@ RSpec.describe AccessRequest, type: :model do it { expect(access_request.approved).to be false } it { expect(access_request.answered).to be true } it { expect(UserMap.count).to eq 0 } - it { expect(enqueued_jobs.count).to eq 0 } + it { expect(Mailboxer::Notification.count).to eq 0 } end end From 40a97a5ae91c801bf307b4d07728f7d6c94ff2e7 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 15 Dec 2016 17:06:51 -0500 Subject: [PATCH 13/16] these are output in the main layout file --- app/views/notifications/_header.html.erb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/views/notifications/_header.html.erb b/app/views/notifications/_header.html.erb index f93f46a6..794bfdf7 100644 --- a/app/views/notifications/_header.html.erb +++ b/app/views/notifications/_header.html.erb @@ -9,10 +9,3 @@ -

    - <% if devise_error_messages? %> - <%= devise_error_messages! %> - <% elsif notice %> - <%= notice %> - <% end %> -

    From 2d920cf66a58e7872ff30b8925fbc6fc4637b76d Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 15 Dec 2016 17:34:42 -0500 Subject: [PATCH 14/16] add maps links to nav locations --- app/assets/stylesheets/apps.css.erb | 7 ++----- .../authorized_applications/index.html.erb | 1 - app/views/layouts/doorkeeper.html.erb | 3 +++ app/views/notifications/_header.html.erb | 3 +++ app/views/notifications/index.html.erb | 4 +--- app/views/notifications/show.html.erb | 9 +++------ app/views/shared/_go_to_maps.html.erb | 3 --- frontend/src/components/Maps/Header.js | 12 ++++++------ 8 files changed, 18 insertions(+), 24 deletions(-) delete mode 100644 app/views/shared/_go_to_maps.html.erb diff --git a/app/assets/stylesheets/apps.css.erb b/app/assets/stylesheets/apps.css.erb index 46fa64b7..5771e366 100644 --- a/app/assets/stylesheets/apps.css.erb +++ b/app/assets/stylesheets/apps.css.erb @@ -2,16 +2,13 @@ position: relative; margin: 0 auto; width: auto; - max-width: 960px; + max-width: 800px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.24); background: #fff; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - border: 1px solid #dcdcdc; box-sizing: border-box; padding: 15px; + font-family: 'din-regular', sans-serif; } .centerContent .page-header { diff --git a/app/views/doorkeeper/authorized_applications/index.html.erb b/app/views/doorkeeper/authorized_applications/index.html.erb index d6391215..42c3127d 100644 --- a/app/views/doorkeeper/authorized_applications/index.html.erb +++ b/app/views/doorkeeper/authorized_applications/index.html.erb @@ -33,6 +33,5 @@ <% end %> -<%= render partial: 'shared/go_to_maps' %> <%= render 'script' %> diff --git a/app/views/layouts/doorkeeper.html.erb b/app/views/layouts/doorkeeper.html.erb index 960502d9..f4696a37 100644 --- a/app/views/layouts/doorkeeper.html.erb +++ b/app/views/layouts/doorkeeper.html.erb @@ -38,6 +38,9 @@
    Authorized Apps
    + +
    Maps +
    diff --git a/app/views/notifications/_header.html.erb b/app/views/notifications/_header.html.erb index 794bfdf7..2507b2ef 100644 --- a/app/views/notifications/_header.html.erb +++ b/app/views/notifications/_header.html.erb @@ -5,6 +5,9 @@
    Notifications
    + +
    Maps +
    diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index 9e37c220..ee075410 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -30,7 +30,7 @@ <% end %> <% if @notifications.count == 0 %>
    - You have ZERO unread notifications. Huzzah! + You have no notifications. More time for dancing.
    <% end %> @@ -41,8 +41,6 @@ <%= paginate @notifications %> <% end %> - - <%= render partial: 'shared/go_to_maps' %> <%= render partial: 'notifications/header' %> diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index a003a0e1..b56fb177 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -2,18 +2,15 @@ <% content_for :mobile_title, 'Notifications' %>
    +
    + <%= link_to 'Back to notifications', notifications_path %> +

    <%= @notification.subject %>

    <%= raw @notification.body %>
    - -
    - <%= link_to 'Back to notifications', notifications_path %> -
    - - <%= render partial: 'shared/go_to_maps' %>
    <%= render partial: 'notifications/header' %> diff --git a/app/views/shared/_go_to_maps.html.erb b/app/views/shared/_go_to_maps.html.erb deleted file mode 100644 index 04c88574..00000000 --- a/app/views/shared/_go_to_maps.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/frontend/src/components/Maps/Header.js b/frontend/src/components/Maps/Header.js index c0a7e1cd..f360e7a5 100644 --- a/frontend/src/components/Maps/Header.js +++ b/frontend/src/components/Maps/Header.js @@ -36,6 +36,12 @@ class Header extends Component {
    + - Date: Thu, 15 Dec 2016 17:41:08 -0500 Subject: [PATCH 15/16] make it look better when its taking up the full screen width --- app/assets/stylesheets/mobile.scss.erb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 8deca0aa..54004dc7 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -18,6 +18,14 @@ width: 390px; } } +/* 800 is the max-width for centerContent */ +@media only screen and (max-width : 800px) { + .centerContent.withPadding { + margin-top: 0; + margin-bottom: 0; + } +} + /* Smartphones (portrait and landscape) ----------- the minimum space that two map cards can fit side by side */ @media only screen and (max-width : 504px) { From 28d960459ef2745a68d17bca4e2db0b4288131bc Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 15 Dec 2016 23:57:37 -0500 Subject: [PATCH 16/16] styling of notifs list --- app/assets/stylesheets/mobile.scss.erb | 18 +++++ app/assets/stylesheets/notifications.scss.erb | 75 +++++++++++++------ app/services/notification_service.rb | 19 ++++- app/views/notifications/index.html.erb | 12 ++- 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 54004dc7..fc34168d 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -8,6 +8,13 @@ } } +/* when this switches to two lines */ +@media only screen and (max-width : 728px) { + .controller-notifications .notificationsPage .notification .notification-read-unread a { + margin-top: -20px !important; + } +} + @media only screen and (max-width : 390px) { .map .mapCard .mobileMetadata { width: 190px; @@ -33,6 +40,17 @@ display: none !important; } + .notificationsPage .page-header { + display: none; + } + + .controller-notifications .notificationsPage .notification .notification-read-unread { + display: block !important; + } + .controller-notifications .notificationsPage .notification .notification-date { + display: none; + } + #mobile_header { display: block; } diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index 5058bc62..16f96407 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -48,20 +48,20 @@ $unread_notifications_dot_size: 8px; padding-top: 15px; } - .notification:first-child { - border-top: none; - } - .notification:last-child { - border-bottom: 1px solid #DCDCDC; - } - .notification { padding: 10px; - border:1px solid #DCDCDC; - border-bottom: none; + position: relative; &:hover { background: #F6F6F6; + + .notification-read-unread { + display:block; + } + + .notification-date { + display: none; + } } & > a { @@ -71,27 +71,58 @@ $unread_notifications_dot_size: 8px; padding-right: 10px; } - .notification-read-unread { + .notification-actor { float: left; - width: 15%; - } - .notification-body, - .notification-subject { - display: inline-block; - vertical-align: top; + img { + width: 32px; + height: 32px; + border-radius: 16px; + } } .notification-body { - margin-left: 15px; + margin-left: 50px; + + .in-bold { + font-family: 'din-medium', Sans-Serif; + } + + .action { + background: #4fb5c0; + color: #FFF; + padding: 2px 6px; + border-radius: 3px; + display: inline-block; + margin: 5px 0; + } + } + + .notification-date { + position: absolute; + top: 50%; + right: 10px; + color: #607d8b; + font-size: 13px; + line-height: 13px; + margin-top: -6px; + } + + .notification-read-unread { + display: none; + float: left; + width: 15%; + + a { + position: absolute; + top: 50%; + margin-top: -10px; + text-align: center; + } } &.unread { - .notification-body, - .notification-subject, - .notification-read-unread { - font-family: 'din-medium', Sans-Serif; - } + background: #EEE; } } diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 41202345..aa919edb 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -9,17 +9,30 @@ class NotificationService def self.access_request(request) body = renderer.render(template: 'map_mailer/access_request_email', locals: { map: request.map, request: request }, layout: false) - request.map.user.notify(request.requested_text, body, request, false, MAILBOXER_CODE_ACCESS_REQUEST) + request.map.user.notify(request.requested_text, body, request, false, MAILBOXER_CODE_ACCESS_REQUEST, true, request.user) end def self.access_approved(request) body = renderer.render(template: 'map_mailer/access_approved_email', locals: { map: request.map }, layout: false) - receipt = request.user.notify(request.approved_text, body, request, false, MAILBOXER_CODE_ACCESS_APPROVED) + receipt = request.user.notify(request.approved_text, body, request, false, MAILBOXER_CODE_ACCESS_APPROVED, true, request.map.user) end def self.invite_to_edit(map, inviter, invited) user_map = UserMap.find_by(user: invited, map: map) body = renderer.render(template: 'map_mailer/invite_to_edit_email', locals: { map: map, inviter: inviter }, layout: false) - invited.notify(map.invited_text, body, user_map, false, MAILBOXER_CODE_INVITE_TO_EDIT) + invited.notify(map.invited_text, body, user_map, false, MAILBOXER_CODE_INVITE_TO_EDIT, true, inviter) + end + + def self.text_for_notification(notification) + if notification.notification_code == MAILBOXER_CODE_ACCESS_REQUEST + map = notification.notified_object.map + 'wants permission to map with you on ' + map.name + '  
    Offer a response
    ' + elsif notification.notification_code == MAILBOXER_CODE_ACCESS_APPROVED + map = notification.notified_object.map + 'granted your request to edit map ' + map.name + '' + elsif notification.notification_code == MAILBOXER_CODE_INVITE_TO_EDIT + map = notification.notified_object.map + 'gave you edit access to map ' + map.name + '' + end end end diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index ee075410..6f66eccd 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -4,18 +4,19 @@
      <% @notifications.each do |notification| %> <% receipt = @receipts.find_by(notification_id: notification.id) %>
    • <%= link_to notification_path(notification.id) do %> -
      - <%= notification.subject %> +
      + <%= image_tag notification.sender.image(:thirtytwo) %>
      - <%= strip_tags(notification.body).truncate(70) %> +
      <%= notification.sender.name %>
      + <%= raw NotificationService.text_for_notification(notification) %>
      <% end %>
      @@ -25,6 +26,9 @@ <%= link_to 'mark as read', mark_read_notification_path(notification.id), remote: true, method: :put %> <% end %>
      +
      + <%= notification.created_at.strftime("%b %d") %> +
    • <% end %>