Skip to content

07. User profiles and publishes

Nick Doiron edited this page Mar 28, 2016 · 9 revisions

In 1batch, there are two URLs for profiles:

/profile - your own profile / editing space

/profile/mapmeld - the profile for another user ("mapmeld"). If you go to your own profile with this URL, you should be redirected back to /profile

These are two separate router.get() calls in app.js. The simpler one is another user's profile, because we only request public information, and we don't require you to be logged in at all:

// someone else's profile
router.get('/profile/:username', async function (ctx) {
  var requser = ctx.req.user;
  if (requser && ctx.params.username.toLowerCase() === requser.name) {
    // redirect to your own profile
    return ctx.redirect('/profile');
  }
  if (ctx.params.username.indexOf('@') > -1) {
    // usernames which are still emails are anonymous
    return printNoExist(ctx);
  }

  // we're looking for the one user with this name (there should only be one)
  // the only public information that's interesting to us is their _id, username, and whether they publicly posted yet
  // your app may need more fields to be public
  var user = await User.findOne({ name: req.params.username.toLowerCase() }, '_id username posted').exec();
  if (!user) { throw 'user does not exist'; }

  // request all images which are published by this user, get source urls
  var images = await Image.find({ published: true, hidden: false, user_id: user.name }).select('_id src').exec();

  // there's a function called responsiveImg which returns proper URLs from Cloudinary
  // we'll talk about it later
  images = images.map(responsiveImg);

  ctx.render('profile', {
    user: user, // the user who created this profile
    images: images, // the user's images
    posted: user.posted, // the user's post date (if they posted)
    forUser: (requser || null), // whether the browsing user is signed in or not
    csrfToken: ctx.csrf
  });
});

responsiveImg is going to be used several times. It takes in an array of Image objects, and asks Cloudinary to return formatted URLs for that image. It also requests the image in multiple sizes, which we can use to show people this page on desktop, mobile, or retina screens. responsiveImg looks like this:

function responsiveImg(img, makeBig) {
  var baseSize = 300;
    if (makeBig) {
    baseSize *= 2;
  }
  var geturl = cloudinary.url;
  var out = {
    _id: img._id,
    src: {
      mini: geturl(img.src, { format: "jpg", width: baseSize * 2/3, height: baseSize * 2/3, crop: "fill" }).replace('http:', ''),
      main: geturl(img.src, { format: "jpg", width: baseSize, height: baseSize, crop: "fill" }).replace('http:', ''),
      retina: geturl(img.src, { format: "jpg", width: baseSize * 2, height: baseSize * 2, crop: "fill" }).replace('http:', '')
    }
  };
  return out;
}

Another detail is we remove 'http:' from the Cloudinary URL. Browsers can throw errors when an HTTPS page has an HTTP resource, and vice versa. Using // means to keep the same protocol as the current page.

Now we can design the view of the profile page itself, using Jade templating:

extends ./layout.jade

block content
  .row
    .col-sm-12.underbar
      if forUser
        a.btn.btn-info.pull-right(href="/logout") Log Out
      else
        a.btn.btn-primary.pull-right(href="/profile") Log In
      h2
        a(href="/feed") 1batch
        small  an app for sharing 8 photos
  .row
    .col-sm-12.col-md-6
      h1= user.name
      if posted
        h4 posted #{posted}
      else
        h4 hasn't posted yet!
  if posted
    .row.profilephotos
      for index in [0, 1, 2, 3]
        if index < images.length
          .col-sm-6.col-lg-3
            img(src=images[index].src.main, srcset="#{images[index].src.main} 1x, #{images[index].src.retina} 2x")
    .row.profilephotos
      for index in [4, 5, 6, 7]
        if index < images.length
          .col-sm-6.col-lg-3
            img(src=images[index].src.main, srcset="#{images[index].src.main} 1x, #{images[index].src.retina} 2x")

Clone this wiki locally