diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..005119baa --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.4.1 diff --git a/jam-ui/cypress/e2e/friends/friends-page.cy.js b/jam-ui/cypress/e2e/friends/friends-page.cy.js index 31c5dfebe..deb9e06da 100644 --- a/jam-ui/cypress/e2e/friends/friends-page.cy.js +++ b/jam-ui/cypress/e2e/friends/friends-page.cy.js @@ -448,6 +448,31 @@ describe('Friends page with data', () => { }); }); + describe('coming from email links', () => { + it.only("opens details sidebar", () => { + cy.visit('/friends?open=details&id=1'); + showSidePanelContent(); + }); + + it.only("opens chat window", () => { + cy.visit('/friends?open=message&id=1'); + cy.get('[data-testid=textMessageModal]') + .should('be.visible') + cy.contains('Send Message to Test User1').should('exist'); + }); + + it.only("sends friend request", () => { + cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' }); + cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } }); + cy.visit('/friends?open=connect&id=1'); + cy.get('[data-testid=profileSidePanel]') + .find('[data-testid=connect]') + .should('be.disabled'); + cy.contains('Success! Your friend request has been sent to Test User1.'); + }); + + }) + describe('filter', () => { const fillFilterForm = () => { //cy.get('[data-testid=btnUpdateSearch]').click(); @@ -582,4 +607,6 @@ describe('Friends page with data', () => { }); }); + + }); diff --git a/jam-ui/cypress/e2e/home/unsubscribe.cy.js b/jam-ui/cypress/e2e/home/unsubscribe.cy.js new file mode 100644 index 000000000..b73f108d2 --- /dev/null +++ b/jam-ui/cypress/e2e/home/unsubscribe.cy.js @@ -0,0 +1,23 @@ +/// + +describe('Unsubscribe from email link', () => { + beforeEach(() =>{ + cy.intercept('POST', /\S+\/unsubscribe_user_match\/\S+/, { statusCode: 200, body: { ok: true } }); + }) + + it("redirect to home page if tok is not provided", () => { + cy.visit('/unsubscribe'); + cy.location('pathname').should('eq', '/'); + }); + + it.only("show unsubscribed message", () => { + cy.visit('/unsubscribe?tok=123'); + cy.location('search') + .should('equal', '?tok=123') + .then((s) => new URLSearchParams(s)) + .invoke('get', 'tok') + .should('equal', '123') + cy.contains("successfully unsubscribed") + }); + +}) \ No newline at end of file diff --git a/jam-ui/src/components/page/JKUnsubscribe.js b/jam-ui/src/components/page/JKUnsubscribe.js index 41e15b353..66174abbf 100644 --- a/jam-ui/src/components/page/JKUnsubscribe.js +++ b/jam-ui/src/components/page/JKUnsubscribe.js @@ -1,19 +1,64 @@ -import React from 'react'; -import { Card, CardBody } from 'reactstrap'; -import FalconCardHeader from '../common/FalconCardHeader'; +import React, { useEffect, useState } from 'react'; +import { Card, CardBody, CardText, CardTitle } from 'reactstrap'; import { useTranslation } from "react-i18next"; +import { useBrowserQuery } from '../../context/BrowserQuery'; +import { useHistory } from "react-router-dom"; + + +const unsubscribeFromNewUsersWeeklyEmail = (token) => { + + const baseUrl = process.env.REACT_APP_LEGACY_BASE_URL + + return new Promise((resolve, reject) => { + fetch(`${baseUrl}/unsubscribe_user_match/${token}`, + { method: 'POST' } + ).then(response => { + if (response.ok) { + resolve(response); + } else { + reject(response) + } + }) + }) +} function JKUnsubscribe() { const {t} = useTranslation() + const queryObj = useBrowserQuery(); + const history = useHistory() + const [success, setSuccess] = useState(false) + + useEffect(() => { + const token = queryObj.get('tok') + if(token){ + unsubscribeFromNewUsersWeeklyEmail(token) + .then((resp) => { + if(resp.ok){ + setSuccess(true) + } + }) + .catch(error => console.error(error)) + }else{ + history.push('/') + } + }, []) return ( - - - -

Unsubscribe from weekly email

- -
-
+ + + + + Unsubscribe From Weekly Email + + { + success? + 'You have successfully unsubscribed from weekly emails on newly joined musicians having low internet latency to you.' : + 'Unsubscribing...' + } + + + + ) } diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index 92f9517e1..b57282d6b 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -153,4 +153,4 @@ export const getLatencyToUsers = (currentUserId, participantIds) => { .then(response => resolve(response)) .catch(error => reject(error)) }) -} \ No newline at end of file +} diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians_match.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians_match.text.erb index 2d06f81dd..32aef7781 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians_match.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians_match.text.erb @@ -1 +1,29 @@ -EMAIL BODY HERE: <%#= @musicians_data.inspect -%> \ No newline at end of file +<% if !@user.anonymous? %> +Hi <%= @user.first_name %>, +<% end %> + +The following musicians have joined JamKazam within the last week and have low internet latency to you that will support enjoyable sessions. If you'd like to make more musical connections, we encourage you to use the links below to send these new users a welcome message and perhaps arrange a session to play together. + +<% + @musicians_data.each do | data | -%> + <% + musicians = data[:musicians] + latencies = data[:latencies] + musicians.each do |musician| + latency = latencies.find{|l| l[:user_id] == musician.id } + -%> + <%= musician.first_name %> <%= musician.last_name %> + Latency To You: <%= latency_info(latency) %> + Last Active On: <%= musician.last_active_timestamp %> ago + <% musician.musician_instruments.each do |mi| -%> + <%= mi.description %> <%= @instrument_proficiencies[mi.proficiency_level.to_s.to_sym] %> + <% end -%> + View Profile: <%= APP_CONFIG.spa_origin -%>/friends?id=<%= musician.id %>&open=details + Send Message: <%= APP_CONFIG.spa_origin -%>/friends?id=<%= musician.id %>&open=message + Send Friend Request: <%= APP_CONFIG.spa_origin -%>/friends?id=<%= musician.id %>&open=connect + <% end -%> + <% end -%> + + To find great musical matches across the entire JamKazam commiunity and make new connections, use the link below to access our musician search feature. This let you filter JamKazammers by latency, instruments, skill level, genre interests, last active day and more. + + Search JamKazam Musicians: <%= APP_CONFIG.spa_origin -%>/friends \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.html.erb b/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.html.erb index f41a043c8..f226c6b54 100644 --- a/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.html.erb +++ b/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.html.erb @@ -33,6 +33,6 @@

You are receiving this email because you created a JamKazam account with the email address <%= @user.email -%>

-

Unsubscribe from this weekly new musician notification

+

Unsubscribe from this weekly new musician notification

\ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.text.erb b/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.text.erb index e69de29bb..03accee00 100644 --- a/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.text.erb +++ b/ruby/lib/jam_ruby/app/views/layouts/user_mailer_beta.text.erb @@ -0,0 +1,11 @@ +<% if @batch_body %> + <%= Nokogiri::HTML(@batch_body).text %> +<% else %> + <%= yield %> +<% end %> + +<% unless @user.nil? || @suppress_user_has_account_footer == true %> +This email was sent to you because you have an account at JamKazam / https://www.jamkazam.com. To unsubscribe: https://www.jamkazam.com/unsubscribe/<%=@user.unsubscribe_token%>. +<% end %> + +Copyright <%= Time.now.year %> JamKazam, Inc. All rights reserved. diff --git a/ruby/lib/jam_ruby/lib/email_new_musician_match.rb b/ruby/lib/jam_ruby/lib/email_new_musician_match.rb index 9eedca2ba..7e7d92c1b 100644 --- a/ruby/lib/jam_ruby/lib/email_new_musician_match.rb +++ b/ruby/lib/jam_ruby/lib/email_new_musician_match.rb @@ -42,9 +42,8 @@ module JamRuby AdminMailer.ugly({to: APP_CONFIG.user_match_monitoring_email, subject:"Weekly user match email sending job started.", - body: "#{email_sending.sent_user_ids.any?? "This is resuming. It was originally started at #{email_sending.created_at} and has been sent to #{email_sending.sent_user_ids.size} user(s) so far." : "This job was started at #{email_sending.created_at}" }. It will send to total of #{ recipients.size } users."}).deliver_now + body: "#{email_sending.sent_user_ids.any?? "This job is resuming. It was originally started at #{email_sending.created_at} and has been sent to #{email_sending.sent_user_ids.size} user(s) so far." : "This job was started at #{email_sending.created_at}" }. It will send to total of #{ recipients.size } users."}).deliver_now - debugger recipients.each do |user| ip_address = user.last_jam_addr.blank?? '127.0.0.1' : IPAddr.new(user.last_jam_addr, Socket::AF_INET).to_s @@ -64,9 +63,9 @@ module JamRuby UserMailer.new_musicians_match(user, matched_musician_data).deliver_now - #user.update_column(:user_match_email_sent_at, Time.now) - #email_sending.sent_user_ids.push(user.id) - #email_sending.save! + user.update_column(:user_match_email_sent_at, Time.now) + email_sending.sent_user_ids.push(user.id) + email_sending.save! end end diff --git a/ruby/spec/jam_ruby/lib/email_new_musician_match_spec.rb b/ruby/spec/jam_ruby/lib/email_new_musician_match_spec.rb index 7ca962b10..bc135f883 100644 --- a/ruby/spec/jam_ruby/lib/email_new_musician_match_spec.rb +++ b/ruby/spec/jam_ruby/lib/email_new_musician_match_spec.rb @@ -35,7 +35,7 @@ describe EmailNewMusicianMatch do JamRuby::EmailNewMusicianMatch.send_new_musicians end - fit "does not sent to whom have not been opted to receive emails" do + it "does not sent to whom have not been opted to receive emails" do JamRuby::EmailNewMusicianMatch.send_new_musicians ActionMailer::Base.deliveries.map{|d| d['to'].to_s }.include?("david@example.com").should be_falsey end @@ -46,10 +46,9 @@ describe EmailNewMusicianMatch do JamRuby::EmailNewMusicianMatch.send_new_musicians end - it "delivers to priority recipients first" do + fit "delivers to priority recipients first" do JamRuby::EmailNewMusicianMatch.send_new_musicians - raise ActionMailer::Base.deliveries.map{|d| d['to'].to_s }.inspect #.include?("seth@example.com").should be_truthy - #ActionMailer::Base.deliveries[1]['to'].to_s.should == "seth@jamkazam.com" #NOTE: the first email is sent to user_match_monitoring_email. The second email should be sent to the first priority user in the priority user list + ActionMailer::Base.deliveries[1]['to'].to_s.should == "seth@jamkazam.com" #NOTE: the first email is sent to user_match_monitoring_email. The second email should be sent to the first priority user in the priority user list end describe "halfway done job" do diff --git a/ruby/spec/mailers/user_mailer_spec.rb b/ruby/spec/mailers/user_mailer_spec.rb index 14b03edfd..918909116 100644 --- a/ruby/spec/mailers/user_mailer_spec.rb +++ b/ruby/spec/mailers/user_mailer_spec.rb @@ -12,11 +12,11 @@ describe UserMailer do let(:user) { FactoryGirl.create(:user) } before(:each) do - stub_const("APP_CONFIG", app_config) + #stub_const("APP_CONFIG", app_config) UserMailer.deliveries.clear end - describe "should send confirm email" do + describe "should send confirm email", focus: true do let (:mail) { UserMailer.deliveries[0] } let (:signup_confirmation_url) { "/confirm" } @@ -180,7 +180,16 @@ describe UserMailer do end end - + describe "sends new musicians email" do + let(:mail) { UserMailer.deliveries[0] } + before(:each) do + UserMailer.new_musicians_match(user, []).deliver_now + end + it { UserMailer.deliveries.length.should == 1 } + it { mail['from'].to_s.should == UserMailer::DEFAULT_SENDER } + it { mail['to'].to_s.should == user.email } + it { mail.multipart?.should == true } # because we send plain + html + end # describe "sends new musicians email" do diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index 2d8131f80..6847087d8 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -445,6 +445,16 @@ JS render text: 'You have been unsubscribed.' end + def unsubscribe_user_match + if params[:user_token].present? && @user = User.read_access_token(params[:user_token]) + @user.subscribe_email_for_user_match = false + @user.save! + render json: { head: :ok } + else + render json: { status: :unprocessable_entity } + end + end + private def _render(action) diff --git a/web/config/routes.rb b/web/config/routes.rb index a61245f4c..13138cd80 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -145,6 +145,7 @@ Rails.application.routes.draw do get '/reset_password_complete' => 'users#reset_password_complete', :as => 'reset_password_complete' match '/unsubscribe/:user_token' => 'users#unsubscribe', via: [:get, :post] + match '/unsubscribe_user_match/:user_token' => 'users#unsubscribe_user_match', via: [:post] # email update get '/confirm_email' => 'users#finalize_update_email', :as => 'confirm_email' # NOTE: if you change this, you break outstanding email changes because links in user inboxes are broken diff --git a/web/spec/requests/users_controller_spec.rb b/web/spec/requests/users_controller_spec.rb index c5f34f5e4..e8ea276a8 100644 --- a/web/spec/requests/users_controller_spec.rb +++ b/web/spec/requests/users_controller_spec.rb @@ -28,6 +28,20 @@ describe UsersController, :type => :api do user.subscribe_email.should eql false end + fit "unsubscribe_user_match" do + user.subscribe_email_for_user_match.should eql false #default value is false + + user.subscribe_email_for_user_match = true #we make it true here for this test + user.save! + + get '/unsubscribe_user_match/' + user.unsubscribe_token + + expect(last_response).to have_http_status(200) + + user.reload + user.subscribe_email_for_user_match.should eql false + end + describe "track_origin" do describe "logged out" do