Skip to content

Commit

Permalink
feat(onboarding): Simplify onboarding process (#409)
Browse files Browse the repository at this point in the history
Closes #163.

## Changes

- after signing up, new users are directly brought to their stream
- a "add a track" button was added at the top of the stream
- a "other recent tracks" link (to `/all`) was added at the top of the steam
- `/button` and `/bookmarklet` lead to the same page (from the onboarding process) => previous versions were removed
- featured users were added, for the welcome email
- bonus: speed up restart with docker-compose 

## Results

<img width="1280" alt="Capture d’écran 2020-11-28 à 17 02 08" src="https://user-images.githubusercontent.com/531781/100520443-ff6ea580-319d-11eb-8049-5d48e6a54050.png">

<img width="1280" alt="Capture d’écran 2020-11-28 à 17 19 11" src="https://user-images.githubusercontent.com/531781/100520425-ebc33f00-319d-11eb-986c-04faa575ac0d.png">

## ... instead of

<img width="1280" alt="Capture d’écran 2020-11-28 à 15 44 14" src="https://user-images.githubusercontent.com/531781/100520454-0bf2fe00-319e-11eb-9b18-593f3749afce.png">

[legacy bookmarklet chrome.pdf](https://github.com/openwhyd/openwhyd/files/5610884/legacy.bookmarklet.chrome.pdf)

[legacy bookmarklet firefox.pdf](https://github.com/openwhyd/openwhyd/files/5610885/legacy.bookmarklet.firefox.pdf)
  • Loading branch information
adrienjoly committed Nov 28, 2020
1 parent 46a7fb1 commit ec40c7d
Show file tree
Hide file tree
Showing 43 changed files with 113 additions and 1,784 deletions.
128 changes: 13 additions & 115 deletions app/controllers/onboarding.js
Original file line number Diff line number Diff line change
@@ -1,127 +1,25 @@
/**
* onboarding controller
* handles the onboarding process (for new users)
* @author adrienjoly, whyd
* explains how to install the bookmarklet or chrome extension
*/

// testing phase 3: send welcome email
// $ curl -v --data "ajax=follow" --cookie "whydSid=4j8OSWWYknxyPlmGmgqURg12AiBoKDQpqt4iU610PT9nKkIkRdlMgHWF9kFMsQEvU" http://openwhyd.org/onboarding

var mongodb = require('../models/mongodb.js');
var userModel = require('../models/user.js');
var followModel = require('../models/follow.js');
var analytics = require('../models/analytics.js');
var notifModel = require('../models/notif.js');
var notifEmails = require('../models/notifEmails.js');

var TEMPLATE_FILE = 'app/templates/onboarding.html';
var mainTemplate = require('../templates/mainTemplate.js');
var templateLoader = require('../templates/templateLoader.js');
const { getSuggestedUsers } = require('../models/featuredUsers.js');

// var MAX_RECOM_USERS = 10;

var templates = {
'bookmarklet-legacy': 'app/templates/onboarding/bookmarklet.html', // old version (still bound to openwhyd.org/bookmarklet and openwhyd.org/button)
};

function makeTemplateRenderer(cb) {
return function (p) {
templateLoader.loadTemplate(templates[p.step] || TEMPLATE_FILE, function (
template
) {
p.content = template.render(p);
cb(p);
});
};
}

var processAjax = {
people: function (_, cb) {
getSuggestedUsers().then(cb);
},
follow: function (p, cb) {
userModel.fetchByUid(p.loggedUser.id, function (user) {
console.log('onboarding, sending welcome email', user.email, user.iBy);
var inviteSender = user.iBy ? mongodb.getUserFromId(user.iBy) : null;
notifEmails.sendRegWelcomeAsync(user, inviteSender);
});

console.log('onboarding, following uids:', p.uids);
var uids = (p.uids || '').split(',');
(function next() {
var uid = uids.pop();
if (uid)
followModel.add(
{
uId: p.loggedUser.id,
uNm: p.loggedUser.name,
tId: uid,
tNm: mongodb.getUserNameFromId(uid),
ctx: 'onb', // onb = onboarding context
},
function () {
console.log('onboarding, followed uid', uid);
notifModel.subscribedToUser(p.loggedUser.id, uid, next);
}
);
})();
cb({ ok: true });
},
};

var processStep = {
people: function (p, render) {
(p.css = p.css || []).push('onboarding.css');
p.bodyClass = 'pgOnboarding stepPeople minimalHeader';
p.stepPeople = true;
render(p);
},
button: function (p, render) {
(p.css = p.css || []).push('onboarding.css');
p.bodyClass = 'pgOnboarding stepButton minimalHeader';
p.stepButton = true;
render(p);
},
'bookmarklet-legacy': function (p, render) {
render(p);
},
};

function handleRequest(p, cb) {
if (p.ajax && processAjax[p.ajax]) {
processAjax[p.ajax](p, cb);
} else {
var lastUrlWord = p.pageUrl.split('?')[0].split('/')[1];
if (lastUrlWord == 'bookmarklet' || lastUrlWord == 'button')
p.step = 'bookmarklet-legacy';

var processor = processStep[p.step];
if (!processor) cb({ error: 'unknown step' });
//cb({redirect:"/"});
else {
processor(p, makeTemplateRenderer(cb));
analytics.addVisit(p.loggedUser, p.pageUrl);
}
}
}

exports.controller = function (request, getParams, response) {
var p =
(request.method.toLowerCase() === 'post' ? request.body : getParams) || {};
request.logToConsole('onboarding.controller ' + request.method, p);
// make sure user is logged in
if (!(p.loggedUser = request.checkLogin(response))) return;
p.pageUrl = request.url;
handleRequest(p, function (r) {
if (!r || r.error) {
r = r || {};
console.log(r.error);
//response.temporaryRedirect("/welcome");
} else if (r.content) r.html = mainTemplate.renderWhydPage(r);

if (r.redirect) response.temporaryRedirect(r.redirect);
else if (r.html) response.renderHTML(r.html);
else response.renderJSON(r);
request.logToConsole('onboarding.controller', getParams);
var loggedUser = request.getUser() || {};
templateLoader.loadTemplate(TEMPLATE_FILE, function (template) {
const p = {
pageUrl: request.url,
loggedUser: loggedUser,
css: ['onboarding.css'],
bodyClass: 'pgOnboarding',
content: template.render(),
};
response.renderHTML(mainTemplate.renderWhydPage(p));
analytics.addVisit(loggedUser.id, request.url);
});
};
10 changes: 9 additions & 1 deletion app/controllers/private/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ var userApi = require('../../controllers/api/user.js');
var htmlRedirect = require('../../templates/logging.js').htmlRedirect;
var genuine = require('../../genuine.js');
const argon2 = require('argon2');
const notifEmails = require('../../models/notifEmails.js');
const mongodb = require('../../models/mongodb.js');

var ENFORCE_GENUINE_SIGNUP_FROM_IOS = false; // TODO: set to true, after x-real-ip header is set by the nginx proxy
var onboardingUrl = '/pick/button';
var onboardingUrl = '/';
var checkInvites = false;

function follow(user, userToFollow, ctx) {
Expand Down Expand Up @@ -183,6 +185,12 @@ exports.registerInvitedUser = function (request, user, response) {
});
} else renderJSON(json);
} else response.renderHTML(htmlRedirect(url));

console.log('sending welcome email', storedUser.email, storedUser.iBy);
const inviteSender = storedUser.iBy
? mongodb.getUserFromId(storedUser.iBy)
: null;
notifEmails.sendRegWelcomeAsync(storedUser, inviteSender);
}

// connect users
Expand Down
10 changes: 1 addition & 9 deletions app/controllers/userLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ exports.controller = function (request, reqParams, response) {
reqParams.showSubscribers = path.endsWith('/subscribers');
reqParams.showSubscriptions = path.endsWith('/subscriptions');
reqParams.pageUrl = request.url;
reqParams.welcome = reqParams.welcome || path.endsWith('/welcome');

function render(data, mimeType) {
if (mimeType)
Expand Down Expand Up @@ -163,14 +162,7 @@ exports.controller = function (request, reqParams, response) {
response.temporaryRedirect(path, paramsObj);
}

//if (path == "/welcome")
// response.temporaryRedirect("/stream?welcome=1", reqParams);

if (
path == '/' ||
request.url.indexOf('/stream') > -1 ||
request.url.indexOf('/welcome') > -1
) {
if (path == '/' || request.url.indexOf('/stream') > -1) {
if (loggedInUser && loggedInUser.id) return renderFriendsLibrary(lib);
else if (reqParams.format == 'json')
return render({ errorCode: 'REQ_LOGIN' });
Expand Down
12 changes: 11 additions & 1 deletion app/models/featuredUsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ exports.getSuggestedUsers = () =>
// TODO: populate this list dynamically, based on latest posted tracks
userModel.fetchUserBios(
[
{ id: '4d94501d1f78ac091dbc9b4d' }, // adrien joly
{ id: '512284f57e91c862b2aaf310' }, // Thomas Besnard
{ id: '53b987c8f1db9430aee884f0' }, // VickyKoka
{ id: '518b5a447e91c862b2adea1a' }, // Israel Lindenbaum
{ id: '5020e4327e91c862b2a7c4d9' }, // Steven TB
{ id: '4fe0f8b57e91c862b2a7c274' }, // Masscut
{ id: '53a8707366491c17b2adcbe3' }, // Tom P.
{ id: '5911f8e098267ba4b34b8424' }, // ROBIT
{ id: '5361647f71eaec19b57037e4' }, // Gérard Duquesnoy
{ id: '544c39c3e04b7b4fca803438' }, // Stefanos
{ id: '5228bc3c7e91c862b2b003af' }, // MrArijog
{ id: '4d94501d1f78ac091dbc9b4d' }, // Adrien Joly
],
resolve
)
Expand Down
36 changes: 10 additions & 26 deletions app/templates/feed.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
display: block;
width: 280px;
height: 192px;
background: url(/images/pickBookmarklet/video-play.png) no-repeat center
center;
background: url(/images/video-play.png) no-repeat center center;
opacity: 0.6;
}

Expand Down Expand Up @@ -229,6 +228,9 @@ <h2>{{name}}</h2>
{{/isUserLogged}}
<h2 class="head-name-tab">Recent tracks from all users</h2>
{{/globalFeed}} {{#homeFeed}}
<a href="/all" style="float: right; margin-top: 20px; margin-right: 20px"
>Other recent tracks</a
>
<h2 class="head-name-tab">Stream</h2>
{{/homeFeed}}
<!-- <h2>{{streamTitle}}</h2> -->
Expand Down Expand Up @@ -278,31 +280,13 @@ <h2 class="head-name-tab">Stream</h2>
<h2><a href="{{url}}">{{name}}</a></h2>
<span>{{nbTracks}} tracks</span>
</div>
{{/items}} {{/showPlaylists}} {{^showPlaylists}} {{{posts}}}
{{#emptyFeed}} {{#ownProfile}}
<div class="emptyFeed post">
<a class="fakePost" href="javascript:modalPostBox(onNewPost)">
<div class="grayOverlay"></div>
<div class="addButton">
<div id="btnAdd" class="btnAdd">
<div></div>
</div>
<span>Add a track</span>
</div>
</a>
<a class="fakePost" href="javascript:modalPostBox(onNewPost)">
<div class="grayOverlay"></div>
<div class="addButton">
<div id="btnAdd" class="btnAdd">
<div></div>
</div>
<span>Add a track</span>
</div>
</a>
{{/items}} {{/showPlaylists}} {{^showPlaylists}}

<div class="addPost">
<a href="javascript:modalPostBox(onNewPost)"> Add a track </a>
</div>
{{/ownProfile}} {{^ownProfile}}
<div class="emptyFeed">No posts yet...</div>
{{/ownProfile}} {{/emptyFeed}} {{#hasMore}}
<!--<div class="post"></div>-->
{{{posts}}} {{#hasMore}}
<div class="btnLoadMore" onclick="loadMore()">
<div>&nbsp;</div>
<span>Load more</span>
Expand Down
43 changes: 2 additions & 41 deletions app/templates/onboarding.html
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
<!--<link href="/css/onboarding.css" rel="stylesheet" type="text/css" />-->
<div class="container">
<div id="progress">
<div id="progress1">
<div><span>1</span></div>
<p>Genres</p>
</div>
<div id="progress2">
<div><span>2</span></div>
<p>Music lovers</p>
</div>
<div id="progress3">
<div><span>3</span></div>
<p>Button</p>
</div>
</div>

{{#stepPeople}}
<a
id="btnNext"
href="javascript:return false;"
style="display: none"
class="no-ajaxy"
>Next</a
>
<h1>Looking for relevant music lovers...</h1>
<div class="whitePanel" id="recomUsers" style="display: none">
<ul class="userList"></ul>
</div>
{{/stepPeople}} {{#stepButton}}
<a id="btnNext" href="/welcome" class="no-ajaxy">Next</a>
<h1>
Save songs from YouTube, SoundCloud and other websites straight to Openwhyd
</h1>
Expand Down Expand Up @@ -284,16 +255,6 @@ <h6>
<a href="http://player.vimeo.com/video/59426075?autoplay=1" target="_blank" title="Watch the video tutorial"></a>
</div>-->
</div>

{{/stepButton}}

<script type="text/javascript" src="/js/onboarding.js"></script>
<script>
/*<![CDATA[*/
$(document).ready(function () {
/*{{#stepPeople}}*/ initOnbPeople(); /*{{/stepPeople}}*/
/*{{#stepButton}}*/ initOnbButton(); /*{{/stepButton}}*/
});
/*]]>*/
</script>
</div>

<script type="text/javascript" src="/js/onboarding.js"></script>
Loading

0 comments on commit ec40c7d

Please sign in to comment.