Skip to content

Instantly share code, notes, and snippets.

@sailsinaction
Last active June 14, 2016 23:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sailsinaction/ae6d7ceabb10f383fcb0fcea65061ebd to your computer and use it in GitHub Desktop.
Save sailsinaction/ae6d7ceabb10f383fcb0fcea65061ebd to your computer and use it in GitHub Desktop.
Chapter 14 Gists
.d8888b. 888 888 d888 d8888 .d8888b. d8b 888
d88P Y88b 888 888 d8888 d8P888 d88P Y88b Y8P 888
888 888 888 888 888 d8P 888 888 888 888
888 88888b. 8888b. 88888b. 888888 .d88b. 888d888 888 d8P 888 888 888 .d8888b 888888 .d8888b
888 888 "88b "88b 888 "88b 888 d8P Y8b 888P" 888 d88 888 888 88888 888 88K 888 88K
888 888 888 888 .d888888 888 888 888 88888888 888 888 8888888888 888888 888 888 888 "Y8888b. 888 "Y8888b.
Y88b d88P 888 888 888 888 888 d88P Y88b. Y8b. 888 888 888 Y88b d88P 888 X88 Y88b. X88
"Y8888P" 888 888 "Y888888 88888P" "Y888 "Y8888 888 8888888 888 "Y8888P88 888 88888P' "Y888 88888P'
888
888
888
module.exports = {
attributes: {
message: {
type: 'string'
},
sender: {
model: 'user'
},
video: {
model: 'video'
}
}
};
/**
* User.js
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
attributes: {
email: {
type: 'string',
email: 'true',
unique: 'true'
},
username: {
type: 'string',
unique: 'true'
},
encryptedPassword: {
type: 'string'
},
gravatarURL: {
type: 'string'
},
deleted: {
type: 'boolean'
},
admin: {
type: 'boolean'
},
banned: {
type: 'boolean'
},
passwordRecoveryToken: {
type: 'string'
},
// tutorials: {
// type: 'json'
// },
// tutorials: {
// collection: 'tutorial',
// },
tutorials: {
collection: 'tutorial',
via: 'owner'
},
ratings: {
collection: 'rating',
via: 'byUser'
},
// Who is following me?
followers: {
collection: 'user',
via: 'following'
},
// Who am I following?
following: {
collection: 'user',
via: 'followers'
},
chats: {
collection: 'chat',
via: 'sender'
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
delete obj.confirmation;
delete obj.encryptedPassword;
return obj;
}
}
};
/**
* Video.js
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
attributes: {
title: {
type: 'string'
},
src: {
type: 'string'
},
lengthInSeconds: {
type: 'integer'
},
tutorialAssoc: {
model: 'tutorial'
},
chats: {
collection: 'chat',
via: 'video'
}
}
};
showVideo: function(req, res) {
// Find the video to play and populate the video `chat` association
Video.findOne({
id: +req.param('id')
})
.populate('chats')
.exec(function (err, foundVideo){
if (err) return res.negotiate(err);
if (!foundVideo) return res.notFound();
//Format each chat with the username, gravatarURL, and created date in timeago format
async.each(foundVideo.chats, function(chat, next){
User.findOne({
id: chat.sender
}).exec(function (err, foundUser){
if (err) return next(err);
chat.username = foundUser.username;
chat.created = DatetimeService.getTimeAgo({date: chat.createdAt});
chat.gravatarURL = foundUser.gravatarURL;
return next();
});
}, function(err) {
if (err) return res.negotiate(err);
/*
_____
| __ \
| |__) |___ ___ _ __ ___ _ __ ___ ___
| _ // _ \/ __| '_ \ / _ \| '_ \/ __|/ _ \
| | \ \ __/\__ \ |_) | (_) | | | \__ \ __/
|_| \_\___||___/ .__/ \___/|_| |_|___/\___|
| |
|_|
*/
// If not logged in
if (!req.session.userId) {
return res.view('show-video', {
me: null,
video: foundVideo,
tutorialId: req.param('tutorialId'),
chats: foundVideo.chats
});
}
// If logged in...
User.findOne({
id: +req.session.userId
}).exec(function (err, foundUser) {
if (err) {
return res.negotiate(err);
}
if (!foundUser) {
sails.log.verbose('Session refers to a user who no longer exists');
return res.view('show-video', {
me: null,
video: foundVideo,
tutorialId: req.param('tutorialId'),
chats: foundVideo.chats
});
}
return res.view('show-video', {
me: {
username: foundUser.username,
gravatarURL: foundUser.gravatarURL,
admin: foundUser.admin
},
video: foundVideo,
tutorialId: req.param('tutorialId'),
chats: foundVideo.chats
});
});
});
});
},
joinChat: function (req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
// Join the chat room for this video (as the requesting socket)
sails.sockets.join(req, 'video'+req.param('id'));
// Video.subscribe(req, req.param('id') );
// Video.watch(req);
return res.ok();
},
chat: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
Chat.create({
message: req.param('message'),
sender: req.session.userId,
video: +req.param('id')
}).exec(function (err, createdChat){
if (err) return res.negotiate(err);
User.findOne({
id: req.session.userId
}).exec(function (err, foundUser){
if (err) return res.negotiate(err);
if (!foundUser) return res.notFound();
// Broadcast WebSocket event to everyone else currently online so their user
// agents can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'), 'chat', {
message: req.param('message'),
username: foundUser.username,
created: 'just now',
gravatarURL: foundUser.gravatarURL
});
return res.ok();
});
});
},
angular.module('brushfire').controller('showVideoPageController', ['$scope', '$http', 'toastr', function($scope, $http, toastr){
/*
____ _____ _
/ __ \ | __ \ | |
| | | |_ __ | |__) |___ _ __ __| | ___ _ __
| | | | '_ \ | _ // _ \ '_ \ / _` |/ _ \ '__|
| |__| | | | | | | \ \ __/ | | | (_| | __/ |
\____/|_| |_| |_| \_\___|_| |_|\__,_|\___|_|
*/
// set-up loading state
$scope.showVideo = {
loading: false
};
$scope.me = window.SAILS_LOCALS.me;
// Get the video id form the current URL path: /tutorials/1/videos/3/show
$scope.fromUrlVideoId = window.location.pathname.split('/')[4];
// Expose chats on the scope so we can render them with ng-repeat.
$scope.chats = window.SAILS_LOCALS.chats;
// Until we've officially joined the chat room, don't allow chats to be sent.
$scope.hasJoinedRoom = false;
// Send a socket request to join the chat room.
io.socket.put('/videos/'+ $scope.fromUrlVideoId + '/join', function (data, JWR) {
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
// TODO
return;
}
// If the server gave us its blessing and indicated that we were
// able to successfully join the room, then we'll set that on the
// scope to allow the user to start sending chats.
//
// Note that, at this point, we'll also be able to start _receiving_ chats.
$scope.hasJoinedRoom = true;
// Because io.socket.get() is not an angular thing, we have to call $scope.$apply()
// in this callback in order for our changes to the scope to actually take effect.
$scope.$apply();
});
// Handle socket events that are fired when a new chat event is sent (.broadcast)
io.socket.on('chat', function (e) {
console.log('new chat received!', e);
// Append the chat we just received
$scope.chats.push({
created: e.created,
username: e.username,
message: e.message,
gravatarURL: e.gravatarURL
});
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply() in
// this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
io.socket.on('typing', function (e) {
console.log('typing!', e);
$scope.usernameTyping = e.username;
$scope.typing = true;
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply()
// in this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
io.socket.on('stoppedTyping', function (e) {
console.log('stoppedTyping!', e);
$scope.typing = false;
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply()
// in this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
/*
_____ ____ __ __ ______ _
| __ \ / __ \| \/ | | ____| | |
| | | | | | | \ / | | |____ _____ _ __ | |_ ___
| | | | | | | |\/| | | __\ \ / / _ \ '_ \| __/ __|
| |__| | |__| | | | | | |___\ V / __/ | | | |_\__ \
|_____/ \____/|_| |_| |______\_/ \___|_| |_|\__|___/
*/
// Send chat to the chat action of the video controller
$scope.sendMessage = function() {
io.socket.post('/videos/'+$scope.fromUrlVideoId+'/chat', {
message: $scope.message
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
// Clear out the chat message field.
// (but rescue its contents first so we can append them)
var messageWeJustChatted = $scope.message;
$scope.message = '';
$scope.$apply();
});
};//</sendMessage>
$scope.whenTyping = function (event) {
io.socket.request({
url: '/videos/'+$scope.fromUrlVideoId+'/typing',
method: 'put'
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
});
};//</whenTyping>
$scope.whenNotTyping = function (event) {
io.socket.request({
url: '/videos/'+$scope.fromUrlVideoId+'/stoppedTyping',
method: 'put'
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
});
};//</whenNotTyping>
}]);
/**
* Brushfire explicit routes
*
*/
module.exports.routes = {
/*************************************************************
* JSON API ENDPOINTS *
*************************************************************/
'PUT /login': 'UserController.login',
'POST /logout': 'UserController.logout',
'GET /logout': 'PageController.logout',
'POST /user/signup': 'UserController.signup',
'PUT /user/remove-profile': 'UserController.removeProfile',
'PUT /user/restore-profile': 'UserController.restoreProfile',
'PUT /user/restore-gravatar-URL': 'UserController.restoreGravatarURL',
'PUT /user/update-profile': 'UserController.updateProfile',
'PUT /user/change-password': 'UserController.changePassword',
'GET /user/admin-users': 'UserController.adminUsers',
'PUT /user/update-admin/:id': 'UserController.updateAdmin',
'PUT /user/update-banned/:id': 'UserController.updateBanned',
'PUT /user/update-deleted/:id': 'UserController.updateDeleted',
'PUT /user/generate-recovery-email': 'UserController.generateRecoveryEmail',
'PUT /user/reset-password': 'UserController.resetPassword',
'PUT /user/follow': 'UserController.follow',
'PUT /user/unfollow': 'UserController.unFollow',
'GET /tutorials': 'TutorialController.browseTutorials',
'POST /tutorials': 'TutorialController.createTutorial',
'POST /tutorials/:tutorialId/videos': 'TutorialController.addVideo',
'PUT /tutorials/:id': 'TutorialController.updateTutorial',
'PUT /tutorials/:id/rate': 'TutorialController.rateTutorial',
'POST /videos/:id/chat': 'VideoController.chat',
'PUT /videos/:id/join': 'VideoController.joinChat',
'PUT /videos/:id/typing': 'VideoController.typing',
'PUT /videos/:id/stoppedTyping': 'VideoController.stoppedTyping',
'DELETE /tutorials/:id': 'TutorialController.deleteTutorial',
'DELETE /videos/:id': 'TutorialController.removeVideo',
'POST /videos/:id/up': 'VideoController.reorderVideoUp',
'POST /videos/:id/down': 'VideoController.reorderVideoDown',
'PUT /videos/:id': 'TutorialController.updateVideo',
/*************************************************************
* Server Rendered HTML Page Endpoints *
*************************************************************/
'GET /profile/followers': 'PageController.profileFollower',
'GET /': 'PageController.home',
'GET /profile/edit': 'PageController.editProfile',
'GET /profile/restore': 'PageController.restoreProfile',
'GET /signin': 'PageController.signin',
'GET /signup': 'PageController.signup',
'GET /administration': 'PageController.administration',
'GET /password-recovery-email': 'PageController.passwordRecoveryEmail',
'GET /password-recovery-email-sent': 'PageController.passwordRecoveryEmailSent',
'GET /password-reset-form/:passwordRecoveryToken': 'PageController.passwordReset',
'GET /tutorials/search': 'TutorialController.searchTutorials',
'GET /tutorials/browse': 'PageController.showBrowsePage',
'GET /tutorials/new': 'PageController.newTutorial',
'GET /tutorials/:id': 'PageController.tutorialDetail',
'GET /tutorials/:id/edit': 'PageController.editTutorial',
'GET /tutorials/:id/videos/new': 'PageController.newVideo',
'GET /tutorials/:tutorialId/videos/:id/edit': 'PageController.editVideo',
'GET /tutorials/:tutorialId/videos/:id/show': 'PageController.showVideo',
'GET /:username/followers': 'PageController.profileFollower',
'GET /:username/following': 'PageController.profileFollowing',
'GET /:username': {
controller: 'PageController',
action: 'profile',
skipAssets: true
}
// 'GET /:username': 'PageController.profile',
};
/**
* VideoController
*
* @description :: Server-side logic for managing videos
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
reorderVideoUp: function(req, res) {
// Look up the video with the specified id
// (and populate the tutorial it belongs to)
Video.findOne({
id: +req.param('id')
})
.populate('tutorialAssoc') // consider renaming this association to `partOfTutorial`
.exec(function (err, foundVideo){
if (err) return res.negotiate(err);
if (!foundVideo) return res.notFound();
// Assure that the owner of the tutorial cannot rate their own tutorial.
// Note that this is a back-up to the front-end which already prevents the UI from being displayed.
if (req.session.userId !== foundVideo.tutorialAssoc.owner) {
return res.forbidden();
}
// Modify the tutorial's `videoOrder` to move the video with the
// specified id up in the list.
// Find the index of the video id within the array.
var indexOfVideo = _.indexOf(foundVideo.tutorialAssoc.videoOrder, +req.param('id'));
// If this is already the first video in the list, consider this a bad request.
// (this should have been prevented on the front-end already, but we're just being safe)
if (indexOfVideo === 0) {
return res.badRequest('This video is already at the top of the list.');
}
// Remove the video id from its current position in the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo, 1);
// Insert the video id at the new position within the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo-1, 0, +req.param('id'));
// Persist the tutorial record back to the database.
foundVideo.tutorialAssoc.save(function (err) {
if (err) return res.negotiate(err);
return res.ok();
});
});
},
reorderVideoDown: function(req, res) {
// Look up the video with the specified id
// (and populate the tutorial it belongs to)
Video.findOne({
id: +req.param('id')
})
.populate('tutorialAssoc') // consider renaming this association to `partOfTutorial`
.exec(function (err, foundVideo){
if (err) return res.negotiate(err);
if (!foundVideo) return res.notFound();
// Assure that the owner of the tutorial cannot rate their own tutorial.
// Note that this is a back-up to the front-end which already prevents the UI from being displayed.
if (req.session.userId !== foundVideo.tutorialAssoc.owner) {
return res.forbidden();
}
// Modify the tutorial's `videoOrder` to move the video with the
// specified id up in the list.
// Find the index of the video id within the array.
var indexOfVideo = _.indexOf(foundVideo.tutorialAssoc.videoOrder, +req.param('id'));
var numberOfTutorials = foundVideo.tutorialAssoc.videoOrder.length;
// If this is already the last video in the list, consider this a bad request.
// (this should have been prevented on the front-end already, but we're just being safe)
if (indexOfVideo === numberOfTutorials) {
return res.badRequest('This video is already at the bottom of the list.');
}
// Remove the video id from its current position in the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo, 1);
// Insert the video id at the new position within the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo+1, 0, +req.param('id'));
// Persist the tutorial record back to the database.
foundVideo.tutorialAssoc.save(function (err) {
if (err) return res.negotiate(err);
return res.ok();
});
});
},
joinChat: function (req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
// Join the chat room for this video (as the requesting socket)
sails.sockets.join(req, 'video'+req.param('id'));
return res.ok();
},
chat: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
Chat.create({
message: req.param('message'),
sender: req.session.userId,
video: +req.param('id')
}).exec(function (err, createdChat){
if (err) return res.negotiate(err);
User.findOne({
id: req.session.userId
}).exec(function (err, foundUser){
if (err) return res.negotiate(err);
if (!foundUser) return res.notFound();
// Broadcast WebSocket event to everyone else currently online so their user
// agents can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'), 'chat', {
message: req.param('message'),
username: foundUser.username,
created: 'just now',
gravatarURL: foundUser.gravatarURL
});
return res.ok();
});
});
},
typing: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
User.findOne({
id: req.session.userId
}).exec(function (err, foundUser){
if (err) return res.negotiate(err);
if (!foundUser) return res.notFound();
// Broadcast socket event to everyone else currently online so their user agents
// can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'), 'typing', {
username: foundUser.username
}, (req.isSocket ? req : undefined) );
return res.ok();
});
},
stoppedTyping: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
// Broadcast socket event to everyone else currently online so their user agents
// can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'),
'stoppedTyping', {}, (req.isSocket ? req : undefined) );
return res.ok();
},
};
/**
* VideoController
*
* @description :: Server-side logic for managing videos
* @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
reorderVideoUp: function(req, res) {
// Look up the video with the specified id
// (and populate the tutorial it belongs to)
Video.findOne({
id: +req.param('id')
})
.populate('tutorialAssoc') // consider renaming this association to `partOfTutorial`
.exec(function (err, foundVideo){
if (err) return res.negotiate(err);
if (!foundVideo) return res.notFound();
// Assure that the owner of the tutorial cannot rate their own tutorial.
// Note that this is a back-up to the front-end which already prevents the UI from being displayed.
if (req.session.userId !== foundVideo.tutorialAssoc.owner) {
return res.forbidden();
}
// Modify the tutorial's `videoOrder` to move the video with the
// specified id up in the list.
// Find the index of the video id within the array.
var indexOfVideo = _.indexOf(foundVideo.tutorialAssoc.videoOrder, +req.param('id'));
// If this is already the first video in the list, consider this a bad request.
// (this should have been prevented on the front-end already, but we're just being safe)
if (indexOfVideo === 0) {
return res.badRequest('This video is already at the top of the list.');
}
// Remove the video id from its current position in the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo, 1);
// Insert the video id at the new position within the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo-1, 0, +req.param('id'));
// Persist the tutorial record back to the database.
foundVideo.tutorialAssoc.save(function (err) {
if (err) return res.negotiate(err);
return res.ok();
});
});
},
reorderVideoDown: function(req, res) {
// Look up the video with the specified id
// (and populate the tutorial it belongs to)
Video.findOne({
id: +req.param('id')
})
.populate('tutorialAssoc') // consider renaming this association to `partOfTutorial`
.exec(function (err, foundVideo){
if (err) return res.negotiate(err);
if (!foundVideo) return res.notFound();
// Assure that the owner of the tutorial cannot rate their own tutorial.
// Note that this is a back-up to the front-end which already prevents the UI from being displayed.
if (req.session.userId !== foundVideo.tutorialAssoc.owner) {
return res.forbidden();
}
// Modify the tutorial's `videoOrder` to move the video with the
// specified id up in the list.
// Find the index of the video id within the array.
var indexOfVideo = _.indexOf(foundVideo.tutorialAssoc.videoOrder, +req.param('id'));
var numberOfTutorials = foundVideo.tutorialAssoc.videoOrder.length;
// If this is already the last video in the list, consider this a bad request.
// (this should have been prevented on the front-end already, but we're just being safe)
if (indexOfVideo === numberOfTutorials) {
return res.badRequest('This video is already at the bottom of the list.');
}
// Remove the video id from its current position in the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo, 1);
// Insert the video id at the new position within the array
foundVideo.tutorialAssoc.videoOrder.splice(indexOfVideo+1, 0, +req.param('id'));
// Persist the tutorial record back to the database.
foundVideo.tutorialAssoc.save(function (err) {
if (err) return res.negotiate(err);
return res.ok();
});
});
},
joinChat: function (req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
// Join the chat room for this video (as the requesting socket)
Video.subscribe(req, req.param('id') );
// Join the chat room for this video (as the requesting socket)
sails.sockets.join(req, 'video'+req.param('id'));
return res.ok();
},
chat: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
Chat.create({
message: req.param('message'),
sender: req.session.userId,
video: +req.param('id')
}).exec(function (err, createdChat){
if (err) return res.negotiate(err);
User.findOne({
id: req.session.userId
}).exec(function (err, foundUser){
if (err) return res.negotiate(err);
if (!foundUser) return res.notFound();
// Broadcast WebSocket event to everyone else currently online so their user
// agents can update the UI for them.
// sails.sockets.broadcast('video'+req.param('id'), 'chat', {
// message: req.param('message'),
// username: foundUser.username,
// created: 'just now',
// gravatarURL: foundUser.gravatarURL
// });
// Send a video event to the video record room
Video.publishUpdate(+req.param('id'), {
message: req.param('message'),
username: foundUser.username,
created: 'just now',
gravatarURL: foundUser.gravatarURL
});
return res.ok();
});
});
},
typing: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
User.findOne({
id: req.session.userId
}).exec(function (err, foundUser){
if (err) return res.negotiate(err);
if (!foundUser) return res.notFound();
// Broadcast socket event to everyone else currently online so their user agents
// can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'), 'typing', {
username: foundUser.username
}, (req.isSocket ? req : undefined) );
return res.ok();
});
},
stoppedTyping: function(req, res) {
// Nothing except socket requests should ever hit this endpoint.
if (!req.isSocket) {
return res.badRequest();
}
// TODO: ^ pull this into a `isSocketRequest` policy
// Broadcast socket event to everyone else currently online so their user agents
// can update the UI for them.
sails.sockets.broadcast('video'+req.param('id'),
'stoppedTyping', {}, (req.isSocket ? req : undefined) );
return res.ok();
},
};
angular.module('brushfire').controller('showVideoPageController', ['$scope', '$http', 'toastr', function($scope, $http, toastr){
/*
____ _____ _
/ __ \ | __ \ | |
| | | |_ __ | |__) |___ _ __ __| | ___ _ __
| | | | '_ \ | _ // _ \ '_ \ / _` |/ _ \ '__|
| |__| | | | | | | \ \ __/ | | | (_| | __/ |
\____/|_| |_| |_| \_\___|_| |_|\__,_|\___|_|
*/
// set-up loading state
$scope.showVideo = {
loading: false
};
$scope.me = window.SAILS_LOCALS.me;
// Get the video id form the current URL path: /tutorials/1/videos/3/show
$scope.fromUrlVideoId = window.location.pathname.split('/')[4];
// Expose chats on the scope so we can render them with ng-repeat.
$scope.chats = window.SAILS_LOCALS.chats;
// Until we've officially joined the chat room, don't allow chats to be sent.
$scope.hasJoinedRoom = false;
// Send a socket request to join the chat room.
io.socket.put('/videos/'+ $scope.fromUrlVideoId + '/join', function (data, JWR) {
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
// TODO
return;
}
// If the server gave us its blessing and indicated that we were
// able to successfully join the room, then we'll set that on the
// scope to allow the user to start sending chats.
//
// Note that, at this point, we'll also be able to start _receiving_ chats.
$scope.hasJoinedRoom = true;
// Because io.socket.get() is not an angular thing, we have to call $scope.$apply()
// in this callback in order for our changes to the scope to actually take effect.
$scope.$apply();
});
// Handle socket events that are fired when a new chat event is sent (.broadcast)
io.socket.on('video', function (e) {
// Append the chat we just received
$scope.chats.push({
created: e.data.created,
username: e.data.username,
message: e.data.message,
gravatarURL: e.data.gravatarURL
});
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply() in
// this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
io.socket.on('typing', function (e) {
console.log('typing!', e);
$scope.usernameTyping = e.username;
$scope.typing = true;
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply()
// in this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
io.socket.on('stoppedTyping', function (e) {
console.log('stoppedTyping!', e);
$scope.typing = false;
// Because io.socket.on() is not an angular thing, we have to call $scope.$apply()
// in this event handler in order for our changes to the scope to actually take effect.
$scope.$apply();
});
/*
_____ ____ __ __ ______ _
| __ \ / __ \| \/ | | ____| | |
| | | | | | | \ / | | |____ _____ _ __ | |_ ___
| | | | | | | |\/| | | __\ \ / / _ \ '_ \| __/ __|
| |__| | |__| | | | | | |___\ V / __/ | | | |_\__ \
|_____/ \____/|_| |_| |______\_/ \___|_| |_|\__|___/
*/
// Send chat to the chat action of the video controller
$scope.sendMessage = function() {
io.socket.post('/videos/'+$scope.fromUrlVideoId+'/chat', {
message: $scope.message
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
// Clear out the chat message field.
// (but rescue its contents first so we can append them)
var messageWeJustChatted = $scope.message;
$scope.message = '';
$scope.$apply();
});
};//</sendMessage>
$scope.whenTyping = function (event) {
io.socket.request({
url: '/videos/'+$scope.fromUrlVideoId+'/typing',
method: 'put'
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
});
};//</whenTyping>
$scope.whenNotTyping = function (event) {
io.socket.request({
url: '/videos/'+$scope.fromUrlVideoId+'/stoppedTyping',
method: 'put'
}, function (data, JWR){
// If something went wrong, handle the error.
if (JWR.statusCode !== 200) {
console.error(JWR);
return;
}
});
};//</whenNotTyping>
}]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment