فهرست منبع

Added private rooms and private playlists. Still needs some things.

KrisVos130 9 سال پیش
والد
کامیت
427934f4f3

+ 74 - 14
app/client/scripts/events.js

@@ -1654,22 +1654,20 @@ Template.privateRoom.events({
         }
     },
     "click #lock": function () {
+        console.log("Lock");
+        console.log(Session.get("privateRoomName"));
         Meteor.call("lockPrivateRoom", Session.get("privateRoomName"));
-        var $parent = $("#lock").parent();
-        $("#lock").remove();
-        $parent.append('<a id="unlock"><i class="material-icons">lock_open</i></a>')
     },
     "click #unlock": function () {
+        console.log("Unlock");
+        console.log(Session.get("privateRoomName"));
         Meteor.call("unlockPrivateRoom", Session.get("privateRoomName"));
-        var $parent = $("#unlock").parent();
-        $("#unlock").remove();
-        $parent.append('<a id="lock"><i class="material-icons">lock_outline</i></a>')
     },
     "click #submit": function () {
         if(Meteor.userId()){
             sendMessageGlobal();
             Meteor.setTimeout(function () {
-                $(".chat-ul").scrollTop(100000);
+                $("#chat-ul").scrollTop(100000);
             }, 1000)
         } else {
             var $toastContent = $('<span>Message not sent. You must log in</span>');
@@ -1683,7 +1681,7 @@ Template.privateRoom.events({
                 if (!$('#chat-message').data('dropdownshown')) {
                     sendMessageGlobal();
                     Meteor.setTimeout(function () {
-                        $(".chat-ul").scrollTop(100000);
+                        $("#chat-ul").scrollTop(100000);
                     }, 1000)
                 }
             } else {
@@ -1692,6 +1690,21 @@ Template.privateRoom.events({
             }
         }
     },
+    "click #add-allowed-submit": function (e) {
+        if(Meteor.userId()){
+            e.preventDefault();
+            Meteor.call("addAllowedToPrivateRoom", Session.get("privateRoomName"), $("#add-allowed").val());
+            $("#add-allowed").val("");
+        } else {
+            var $toastContent = $('<span>User not added. You must log in</span>');
+            Materialize.toast($toastContent, 2000);
+        }
+    },
+    "keyup #add-allowed": function (e) {
+        if (e.type === "keyup" && e.which === 13) {
+            $("#add-allowed-submit").click();
+        }
+    },
     "click #vote-skip": function () {
         Meteor.call("votePrivateSkip", Session.get("privateRoomName"), function (err, res) {
             $("#vote-skip").addClass("disabled");
@@ -1713,15 +1726,9 @@ Template.privateRoom.events({
     },
     "click #play": function () {
         Meteor.call("resumePrivateRoom", Session.get("privateRoomName"));
-        var $parent = $("#play").parent();
-        $("#play").remove();
-        $parent.append('<a id="pause"><i class="material-icons">pause</i></a>')
     },
     "click #pause": function () {
         Meteor.call("pausePrivateRoom", Session.get("privateRoomName"));
-        var $parent = $("#pause").parent();
-        $("#pause").remove();
-        $parent.append('<a id="play"><i class="material-icons">play_arrow</i></a>')
     },
     "click #skip": function () {
         Meteor.call("skipPrivateSong", Session.get("privateRoomName"));
@@ -1730,6 +1737,59 @@ Template.privateRoom.events({
         Meteor.setTimeout(function(){
             $(".dropdown-button").click();
         }, 10);
+    },
+    "click .remove-allowed": function(e) {
+        var user = $(e.target).data("user");
+        if (user === undefined) {
+            user = $(e.target).parent().data("user");
+        }
+        Meteor.call("removeAllowedFromPrivateRoom", Session.get("privateRoomName"), user);
+    },
+    "click .edit-playlist-button": function(e) {
+        if ($(e.target).hasClass("edit-playlist-button")) {
+            Session.set("editingPlaylistName", $(e.target).data("playlist"));
+        }
+    },
+    "click #add_playlist_video_submit": function() {
+        var id = $("#add_playlist_video").val();
+        var pp = Session.get("editingPlaylistName");
+        Meteor.call("addVideoToPrivatePlaylist", pp, id);
+        $("#add_playlist_video").val("");
+    },
+    "click .remove_playlist_button": function(e) {
+        var id = $(e.target).data("id");
+        Meteor.call("removeVideoFromPrivatePlaylist", Session.get("editingPlaylistName"), id);
+    },
+    "click #delete_playlist": function() {
+        Meteor.call("deletePrivatePlaylist", Session.get("editingPlaylistName"));
+        $("#edit_playlist_modal").closeModal();
+    },
+    "click #create_playlist_submit": function() {
+        var name = $("#create_playlist_name").val();
+        var displayName = $("#create_playlist_display_name").val();
+        Meteor.call("createPrivatePlaylist", name, displayName);
+        $("#create_playlist_modal").closeModal();
+        $("#create_playlist_name").val("");
+        $("#create_playlist_display_name").val("");
+    },
+    "click .select-playlist": function(e) {
+        e.preventDefault();
+        $("#edit_playlist_modal").closeModal();
+        var name = $(e.target).data("playlist");
+        Meteor.call("setPlaylistForPrivateRoom", Session.get("privateRoomName"), name);
+    }
+});
+
+Template.home.events({
+    "click #create_private_room_submit": function () {
+         var name = $("#create_private_room_name").val();
+         var displayName = $("#create_private_room_display_name").val();
+         var description = $("#create_private_room_description").val();
+         Meteor.call("createPrivateRoom", name, displayName, true, description);
+         $("#create_private_room_name").val("");
+         $("#create_private_room_display_name").val("");
+         $("#create_private_room_description").val("");
+        $("#create_private_room").closeModal();
     }
 });
 // Settings Template

+ 69 - 9
app/client/scripts/helpers.js

@@ -110,6 +110,20 @@ Template.home.helpers({
         var type = this.type;
         var userNum = Rooms.findOne({type: type}).users;
         return userNum;
+    },
+    currentPrivateSong: function () {
+        var name = this.name;
+        var room = PrivateRooms.findOne({name: name});
+        if (room !== undefined) {
+            return room.currentSong;
+        } else {
+            return false;
+        }
+    },
+    userPrivateNum: function () {
+        var name = this.name;
+        var userNum = PrivateRooms.findOne({name: name}).users;
+        return userNum;
     }
 });
 
@@ -521,6 +535,20 @@ Template.room.helpers({
 });
 
 Template.privateRoom.helpers({
+    privateRoomOwnerName: function() {
+        var room = PrivateRooms.findOne({name: Session.get("privateRoomName")});
+        if (room !== undefined) {
+            return Meteor.users.findOne(room.owner).profile.username;
+        } else {
+            return "";
+        }
+    },
+    editingPlaylist: function() {
+        return PrivatePlaylists.findOne({owner: Meteor.userId(), name: Session.get("editingPlaylistName")});
+    },
+    isPlaylistSelected: function(roomName, playlistName) {
+        return PrivateRooms.findOne({name: roomName}).playlist === playlistName;
+    },
     globalChat: function () {
         Meteor.setTimeout(function () {
             var elem = document.getElementById('global-chat');
@@ -535,11 +563,28 @@ Template.privateRoom.helpers({
         var id = parts.pop().toLowerCase();
         return PrivateRooms.findOne({name: id}).displayName;
     },
+    name: function () {
+        var parts = location.href.split('/');
+        var id = parts.pop().toLowerCase();
+        return id;
+    },
     users: function () {
         var parts = location.href.split('/');
         var id = parts.pop().toLowerCase();
         return PrivateRooms.findOne({name: id}).users;
     },
+    allowed: function () {
+        var parts = location.href.split('/');
+        var id = parts.pop().toLowerCase();
+        var arr = [];
+        PrivateRooms.findOne({name: id}).allowed.forEach(function(allowed) {
+            arr.push({name: Meteor.users.findOne(allowed).profile.username, id: allowed});
+        });
+        return arr;
+    },
+    playlists: function () {
+        return PrivatePlaylists.find({owner: Meteor.userId()});
+    },
     title: function () {
         return Session.get("title");
     },
@@ -550,23 +595,38 @@ Template.privateRoom.helpers({
         return Session.get("state") === "paused";
     },
     private: function () {
-        return 1;
-        //return Rooms.findOne({type: Session.get("type")}).private === true;
+        var room = PrivateRooms.findOne({name: Session.get("privateRoomName")});
+        if (room !== undefined) {
+            return room.private;
+        } else {
+            return 1;
+        }
+    },
+    playing: function() {
+        return Session.get("state") === "playing";
     },
     currentSong: function(){
         return Session.get("currentSong");
     },
     votes: function () {
-        return PrivateRooms.findOne({name: Session.get("privateRoomName")}).votes;
+        var room = PrivateRooms.findOne({name: Session.get("privateRoomName")});
+        if (room !== undefined) {
+            return room.votes;
+        } else {
+            return 0;
+        }
     },
     usersInRoom: function(){
         var userList = [];
-        var roomUserList = PrivateRooms.findOne({type: Session.get("privateRoomName")}).userList;
-        roomUserList.forEach(function(user){
-            if(userList.indexOf(user) === -1){
-                userList.push(user);
-            }
-        })
+        var room = PrivateRooms.findOne({name: Session.get("privateRoomName")});
+        if (room !== undefined) {
+            var roomUserList = room.userList;
+            roomUserList.forEach(function (user) {
+                if (userList.indexOf(user) === -1) {
+                    userList.push(user);
+                }
+            })
+        }
         return userList;
     }
 });

+ 36 - 0
app/client/scripts/main.js

@@ -21,8 +21,11 @@ Deps.autorun(function() {
     Meteor.subscribe("songs");
     Meteor.subscribe("alerts");
     Meteor.subscribe("rooms");
+    Meteor.subscribe("private_rooms");
+    Meteor.subscribe("private_playlists");
     Meteor.subscribe("news");
     Meteor.subscribe("userData", Meteor.userId());
+    Meteor.subscribe("usernames");
 });
 
 Handlebars.registerHelper("isAdmin", function(argument){
@@ -41,6 +44,30 @@ Handlebars.registerHelper("isModerator", function(argument){
     }
 });
 
+Handlebars.registerHelper("isAllowedInPrivateRoom", function(name){
+    var room = PrivateRooms.findOne({name: name});
+    if (Meteor.user() &&
+        Meteor.user().profile &&
+        room &&
+        ((Meteor.user().profile.rank === "admin" || Meteor.user().profile.rank === "moderator") || (room.allowed.indexOf(Meteor.userId()) !== -1 || room.owner === Meteor.userId()))) {
+        return true;
+    } else {
+        return false;
+    }
+});
+
+Handlebars.registerHelper("isPrivateRoomOwner", function(name){
+    var room = PrivateRooms.findOne({name: name});
+    if (Meteor.user() &&
+        Meteor.user().profile &&
+        room &&
+        ((Meteor.user().profile.rank === "admin" || Meteor.user().profile.rank === "moderator") || room.owner === Meteor.userId())) {
+        return true;
+    } else {
+        return false;
+    }
+});
+
 Handlebars.registerHelper("initials", function(argument){
     var user = Meteor.user();
     if (user !== undefined) {
@@ -55,6 +82,11 @@ Handlebars.registerHelper("rooms", function(){
     return Rooms.find({});
 });
 
+Handlebars.registerHelper("privateRooms", function(){
+    return PrivateRooms.find({});
+});
+
+
 Handlebars.registerHelper("songs", function(){
     return Songs.find({});
 });
@@ -67,6 +99,10 @@ Handlebars.registerHelper('isLoggedIn', function(path) {
     return Meteor.userId();
 });
 
+Handlebars.registerHelper('getUsernameFromId', function(id) {
+    return Meteor.users.findOne(id).profile.username;
+});
+
 UI.registerHelper("formatTime", function(seconds) {
     var d = moment.duration(parseInt(seconds), 'seconds');
     return d.minutes() + ":" + ("0" + d.seconds()).slice(-2);

+ 116 - 48
app/client/scripts/onCreated.js

@@ -219,35 +219,41 @@ Template.room.onCreated(function () {
                     }, 500));
                 } else {
                     if (YTPlayer === undefined) {
-                        YTPlayer = new YT.Player("player", {
-                            height: 270,
-                            width: 480,
-                            videoId: currentSong.id,
-                            playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
-                            events: {
-                                'onReady': function (event) {
-                                    if (currentSong.skipDuration === undefined) {
-                                        currentSong.skipDuration = 0;
-                                    }
-                                    event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
-                                    event.target.playVideo();
-                                    event.target.setVolume(volume);
-                                    resizeSeekerbar();
-                                },
-                                'onStateChange': function (event) {
-                                    if (Session.get("YTLoaded")) {
-                                        if (event.data == YT.PlayerState.PAUSED && Session.get("state") === "playing") {
-                                            event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
-                                            event.target.playVideo();
+                        if (YT !== undefined && YT.Player !== undefined) {
+                            YTPlayer = new YT.Player("player", {
+                                height: 270,
+                                width: 480,
+                                videoId: currentSong.id,
+                                playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
+                                events: {
+                                    'onReady': function (event) {
+                                        if (currentSong.skipDuration === undefined) {
+                                            currentSong.skipDuration = 0;
                                         }
-                                        if (event.data == YT.PlayerState.PLAYING && Session.get("state") === "paused") {
-                                            event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
-                                            event.target.pauseVideo();
+                                        event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
+                                        event.target.playVideo();
+                                        event.target.setVolume(volume);
+                                        resizeSeekerbar();
+                                    },
+                                    'onStateChange': function (event) {
+                                        if (Session.get("YTLoaded")) {
+                                            if (event.data == YT.PlayerState.PAUSED && Session.get("state") === "playing") {
+                                                event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
+                                                event.target.playVideo();
+                                            }
+                                            if (event.data == YT.PlayerState.PLAYING && Session.get("state") === "paused") {
+                                                event.target.seekTo(Number(currentSong.skipDuration) + getTimeElapsed() / 1000);
+                                                event.target.pauseVideo();
+                                            }
                                         }
                                     }
                                 }
-                            }
-                        });
+                            });
+                        } else {
+                            setTimeout(function() {
+                                startSong();
+                            }, 500);
+                        }
                     } else {
                         YTPlayer.loadVideoById(currentSong.id);
                         if (currentSong.skipDuration === undefined) {
@@ -445,32 +451,38 @@ Template.privateRoom.onCreated(function () {
                     }, 500));
                 } else {
                     if (YTPlayer === undefined) {
-                        YTPlayer = new YT.Player("player", {
-                            height: 270,
-                            width: 480,
-                            videoId: currentSong.id,
-                            playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
-                            events: {
-                                'onReady': function (event) {
-                                    event.target.seekTo(getTimeElapsed() / 1000);
-                                    event.target.playVideo();
-                                    event.target.setVolume(volume);
-                                    resizeSeekerbar();
-                                },
-                                'onStateChange': function (event) {
-                                    if (Session.get("YTLoaded")) {
-                                        if (event.data == YT.PlayerState.PAUSED && Session.get("state") === "playing") {
-                                            event.target.seekTo(getTimeElapsed() / 1000);
-                                            event.target.playVideo();
-                                        }
-                                        if (event.data == YT.PlayerState.PLAYING && Session.get("state") === "paused") {
-                                            event.target.seekTo(getTimeElapsed() / 1000);
-                                            event.target.pauseVideo();
+                        if (YT !== undefined && YT.Player !== undefined) {
+                            YTPlayer = new YT.Player("player", {
+                                height: 270,
+                                width: 480,
+                                videoId: currentSong.id,
+                                playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
+                                events: {
+                                    'onReady': function (event) {
+                                        event.target.seekTo(getTimeElapsed() / 1000);
+                                        event.target.playVideo();
+                                        event.target.setVolume(volume);
+                                        resizeSeekerbar();
+                                    },
+                                    'onStateChange': function (event) {
+                                        if (Session.get("YTLoaded")) {
+                                            if (event.data == YT.PlayerState.PAUSED && Session.get("state") === "playing") {
+                                                event.target.seekTo(getTimeElapsed() / 1000);
+                                                event.target.playVideo();
+                                            }
+                                            if (event.data == YT.PlayerState.PLAYING && Session.get("state") === "paused") {
+                                                event.target.seekTo(getTimeElapsed() / 1000);
+                                                event.target.pauseVideo();
+                                            }
                                         }
                                     }
                                 }
-                            }
-                        });
+                            });
+                        } else {
+                            setTimeout(function() {
+                                startSong();
+                            }, 500);
+                        }
                     } else {
                         YTPlayer.loadVideoById(currentSong.id);
                         YTPlayer.seekTo(getTimeElapsed() / 1000);
@@ -550,6 +562,12 @@ Template.privateRoom.onCreated(function () {
             else if($("#users-slide-out").css("right") === "0px"){
                 $("#users-slideout").sideNav("hide");
             }
+            else if($("#allowed-slide-out").css("right") === "0px"){
+                $("#allowed-slideout").sideNav("hide");
+            }
+            else if($("#playlists-slide-out").css("right") === "0px"){
+                $("#playlists-slideout").sideNav("hide");
+            }
             var marginRightWidth = ($(document).width() - $(".container").width()) / 2 + "px";
             $(".room-container").css("margin-right", "370px")
             if($("#playlist-slide-out").css("right") === "0px"){
@@ -563,6 +581,12 @@ Template.privateRoom.onCreated(function () {
             else if($("#users-slide-out").css("right") === "0px"){
                 $("#users-slideout").sideNav("hide");
             }
+            else if($("#allowed-slide-out").css("right") === "0px"){
+                $("#allowed-slideout").sideNav("hide");
+            }
+            else if($("#playlists-slide-out").css("right") === "0px"){
+                $("#playlists-slideout").sideNav("hide");
+            }
             var marginRightWidth = ($(document).width() - $(".container").width()) / 2 + "px";
             $(".chat-ul").scrollTop(1000000);
             $(".room-container").css("margin-right", "370px")
@@ -577,12 +601,56 @@ Template.privateRoom.onCreated(function () {
             else if($("#chat-slide-out").css("right") === "0px"){
                 $("#chat-slideout").sideNav("hide");
             }
+            else if($("#allowed-slide-out").css("right") === "0px"){
+                $("#allowed-slideout").sideNav("hide");
+            }
+            else if($("#playlists-slide-out").css("right") === "0px"){
+                $("#playlists-slideout").sideNav("hide");
+            }
             var marginRightWidth = ($(document).width() - $(".container").width()) / 2 + "px";
             $(".room-container").css("margin-right", "370px")
             if($("#users-slide-out").css("right") === "0px"){
                 $(".room-container").css("margin-right", marginRightWidth);
             }
         });
+        $("#allowed-slideout").on("click", function(){
+            if($("#playlist-slide-out").css("right") === "0px"){
+                $("#playlist-slideout").sideNav("hide");
+            }
+            else if($("#chat-slide-out").css("right") === "0px"){
+                $("#chat-slideout").sideNav("hide");
+            }
+            else if($("#users-slide-out").css("right") === "0px"){
+                $("#users-slideout").sideNav("hide");
+            }
+            else if($("#playlists-slide-out").css("right") === "0px"){
+                $("#playlists-slideout").sideNav("hide");
+            }
+            var marginRightWidth = ($(document).width() - $(".container").width()) / 2 + "px";
+            $(".room-container").css("margin-right", "370px")
+            if($("#allowed-slide-out").css("right") === "0px"){
+                $(".room-container").css("margin-right", marginRightWidth);
+            }
+        });
+        $("#playlists-slideout").on("click", function(){
+            if($("#playlist-slide-out").css("right") === "0px"){
+                $("#playlist-slideout").sideNav("hide");
+            }
+            else if($("#chat-slide-out").css("right") === "0px"){
+                $("#chat-slideout").sideNav("hide");
+            }
+            else if($("#users-slide-out").css("right") === "0px"){
+                $("#users-slideout").sideNav("hide");
+            }
+            else if($("#allowed-slide-out").css("right") === "0px"){
+                $("#allowed-slideout").sideNav("hide");
+            }
+            var marginRightWidth = ($(document).width() - $(".container").width()) / 2 + "px";
+            $(".room-container").css("margin-right", "370px")
+            if($("#playlists-slide-out").css("right") === "0px"){
+                $(".room-container").css("margin-right", marginRightWidth);
+            }
+        });
         $("body").on("click", function(e){
 
         });

+ 1 - 1
app/client/scripts/routes.js

@@ -194,7 +194,7 @@ Router.route("/private/:name", {
         var room = PrivateRooms.findOne({name: this.params.name});
         if (room !== undefined) {
             if ((room.private === true && user !== undefined && user.profile !== undefined && (user.profile.rank === "admin" ||
-                user.profile.rank === "moderator")) || room.private === false || (user !== undefined && user.profile !== undefined && room.allowed.includes(user.profile))) {
+                user.profile.rank === "moderator")) || room.private === false || (user !== undefined && user.profile !== undefined && room.allowed.includes(Meteor.userId())) || room.owner === Meteor.userId()) {
                 Session.set("type", this.params.type);
                 this.render("privateRoom");
             } else {

+ 1 - 1
app/client/stylesheets/app.css

@@ -344,7 +344,7 @@ hr{
     height: 50px;
 }
 
-#submit{
+#submit, #add-allowed-submit, #add-playlist-modal-button{
     color: white;
     height: 36px;
     line-height: 36px;

+ 0 - 34
app/client/templates/dashboard.html

@@ -1,34 +0,0 @@
-<template name="dashboard">
-    <div class="row">
-        {{#each rooms}}
-            {{#if private}}
-                {{#if isModerator}}
-                    <div class="col-md-4 col-sm-6 col-xs-12">
-                        <div class="station" style="background-color: gray">
-                            <h3>{{display}}</h3>
-                            {{#with type=type}}
-                                <h5>{{currentSong.song.title}}</h5>
-                                <h6>{{currentSong.song.artist}}</h6>
-                            {{/with}}
-                            <a href="/{{type}}" class="station_link"></a>
-                        </div>
-                    </div>
-                {{/if}}
-            {{else}}
-                <div class="col-md-4 col-sm-6 col-xs-12">
-                    <div class="station">
-                        {{#with type=type}}
-                            <i class="fa fa-user user-num"></i> <span class="user-num">{{userNum}}</span>
-                        {{/with}}
-                        <h3>{{display}}</h3>
-                        {{#with type=type}}
-                            <h5>{{currentSong.song.title}}</h5>
-                            <h6>{{currentSong.song.artist}}</h6>
-                        {{/with}}
-                        <a href="/{{type}}" class="station_link"></a>
-                    </div>
-                </div>
-            {{/if}}
-        {{/each}}
-    </div>
-</template>

+ 82 - 0
app/client/templates/home.html

@@ -2,6 +2,8 @@
     {{> header}}
     <main>
         {{> alerts}}
+            <h3 class="center-align">Official Rooms</h3>
+            <hr class="center-block" style="width: 99%">
             <div class="row">
                 {{#each rooms}}
                     {{#if private}}
@@ -51,6 +53,86 @@
                     {{/if}}
                 {{/each}}
             </div>
+            <h3 class="center-align">Private Rooms <a href="#create_private_room" id="create_private_room_modal_button"><i class="material-icons">add</i></a></h3>
+            <hr class="center-block" style="width: 99%">
+            <div class="row">
+                {{#each privateRooms}}
+                    {{#if private}}
+                        {{#if isAllowedInPrivateRoom name}}
+                            <div class="col s6 m4 l2 light-blue">
+                                <div class="card hoverable">
+                                    <div class="card-image waves-effect waves-block waves-light">
+                                        <a href=/private/{{name}}>
+                                        <img src='http://static.boredpanda.com/blog/wp-content/uploads/2014/04/amazing-fox-photos-182.jpg'>
+                                        <figcaption class="caption center-align">
+                                            <h5>{{currentPrivateSong.song.title}}</h5>
+                                        </figcaption>
+                                        </a>
+                                    </div>
+                                    <div class="card-content">
+                                        <span class="card-title grey-text text-darken-4">{{displayName}}</span><div><span class="user-num">{{userPrivateNum}}</span> <i class="material-icons">perm_identity</i></div>
+                                        <p>{{roomDesc}}</p>
+                                    </div>
+                                    <div class="card-action">
+                                        <a href=/private/{{name}} class="teal-text text-accent-4">Join Room</a>
+                                    </div>
+                                </div>
+                            </div>
+                        {{/if}}
+                    {{else}}
+                        <div class="col s6 m4 l2">
+                            <div class="card hoverable">
+                                <div class="card-image waves-effect waves-block waves-light">
+                                    <a href=/private/{{name}}>
+                                        <img src='http://static.boredpanda.com/blog/wp-content/uploads/2014/04/amazing-fox-photos-182.jpg'>
+                                        <figcaption class="caption center-align">
+                                            <h5>{{currentPrivateSong.song.title}}</h5>
+                                        </figcaption>
+                                    </a>
+                                </div>
+                                <div class="card-content">
+                                    <span class="card-title grey-text text-darken-4">{{display}}</span><div><span class="user-num">{{userPrivateNum}}</span> <i class="material-icons">perm_identity</i></div>
+                                    <p>{{roomDesc}}</p>
+                                </div>
+                                <div class="card-action">
+                                    <a href=/private/{{name}} class="teal-text text-accent-4">Join Room</a>
+                                </div>
+                            </div>
+                        </div>
+                    {{/if}}
+                {{/each}}
+            </div>
     </main>
     {{> footer}}
+
+    <div id="create_private_room" class="modal">
+        <div class="modal-content">
+            <h4>Create Modal</h4>
+            <div class="input-field">
+                <input id="create_private_room_name" type="text">
+                <label for="create_private_room_name">Name</label>
+            </div>
+            <div class="input-field">
+                <input id="create_private_room_display_name" type="text">
+                <label for="create_private_room_display_name">Display Name</label>
+            </div>
+            <div class="input-field">
+                <input id="create_private_room_description" type="text">
+                <label for="create_private_room_description">Description</label>
+            </div>
+            <button class="btn waves-effect waves-light" id="create_private_room_submit">Create</button>
+        </div>
+        <div class="divider"></div>
+        <div class="modal-footer">
+            <a class="modal-action modal-close waves-effect btn">Close</a>
+        </div>
+    </div>
+    <script>
+        $("#create_private_room_modal_button").leanModal({
+            dismissible: true,
+            opacity: .5,
+            in_duration: 500,
+            out_duration: 200
+        });
+    </script>
 </template>

+ 134 - 11
app/client/templates/privateRoom.html

@@ -4,18 +4,18 @@
             <div class="nav-wrapper teal accent-4">
                 <ul class="left hide-on-med-and-down">
                     <li><a href="/"><i class="material-icons">home</i></a></li>
-                    <li><a href="#add_song_modal" class="tooltipped" data-position="bottom" data-delay="50" data-tooltip="Request a song" id="add-song-modal-button"><i class="material-icons">playlist_add</i></a></li>
-                    <li><a href="#report_modal" class="tooltipped" data-position="bottom" data-delay="50" data-tooltip="Flag a song" id="report-modal-button"><i class="material-icons">flag</i></a></li>
                     <li><a id="vote-skip" class="tooltipped" data-position="bottom" data-delay="50" data-tooltip="Vote to skip this song"><i class="material-icons left">skip_next</i>{{votes}}</a></li>
-                    {{#if isAdmin}}
-                        <li><a class='dropdown-button' data-activates='admin-dropdown'><i class="material-icons">control_point</i></a></li>
+                    {{#if isPrivateRoomOwner name}}
+                        <li><a class='dropdown-button' data-activates='control-dropdown'><i class="material-icons">control_point</i></a></li>
                     {{/if}}
                 </ul>
-                <span class="brand-logo center">{{privateRoomDisplayName}}</span>
+                <span class="brand-logo center">{{privateRoomDisplayName}} <small>(by {{privateRoomOwnerName}})</small></span>
                 <ul class="right hide-on-med-and-down">
                     <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Playlist" id="playlist-slideout" data-activates="playlist-slide-out" class="tooltipped header-collapse"><i class="material-icons">queue_music</i></a></li>
                     <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Chat" id="chat-slideout" data-activates="chat-slide-out" class="tooltipped header-collapse"><i class="material-icons">chat</i></a></li>
                     <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Users" id="users-slideout" data-activates="users-slide-out" class="tooltipped header-collapse"><i class="material-icons">people</i></a></li>
+                    <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Allowed" id="allowed-slideout" data-activates="allowed-slide-out" class="tooltipped header-collapse"><i class="material-icons">people</i></a></li>
+                    <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Playlists" id="playlists-slideout" data-activates="playlists-slide-out" class="tooltipped header-collapse"><i class="material-icons">queue_music</i></a></li>
                 </ul>
             </div>
         </nav>
@@ -53,7 +53,7 @@
     <!--Chat slideout-->
     <div id="chat-slide-out" class="side-nav room-slideout">
         <h5>Chat</h5>
-        <ul class="chat-ul">
+        <ul class="list-ul" id="chat-ul">
             {{#each globalChat}}
                 {{#emojione}}
                     <li class="chat-message" style="line-height: 30px">
@@ -88,14 +88,128 @@
             {{/each}}
         </ul>
     </div>
-    <!--Admin room controls-->
-    <ul id='admin-dropdown' style="background-color: #00bfa5 !important; display: none">
-        <li><a id="pause"><i class="material-icons">pause</i></a></li>
+
+    <div id="allowed-slide-out" class="side-nav room-slideout">
+        <h5>Allowed</h5>
+        <ul class="list-ul" id="allowed-ul">
+            {{#each allowed}}
+                <li>
+                    <a href=/u/{{this.name}} target="_blank">
+                        {{this.name}}
+                        {{#if isPrivateRoomOwner name}}
+                            <a href="#" class="right remove-allowed" data-user={{this.id}}>
+                                <i class="material-icons" style="line-height: 64px;">remove</i>
+                            </a>
+                        {{/if}}
+                    </a>
+                </li>
+            {{/each}}
+        </ul>
+        {{#if isPrivateRoomOwner name}}
+        <div>
+            <div class="row" id="chat-input-div">
+                <div class="input-field col s12">
+                    <input id="add-allowed" type="text">
+                    <label for="add-allowed">Add user</label>
+                </div>
+            </div>
+            <a id="add-allowed-submit" class="waves-effect waves-light btn">Add</a>
+        </div>
+        {{/if}}
+    </div>
+
+    <div id="playlists-slide-out" class="side-nav room-slideout">
+        <h5>Playlists</h5>
+        <ul class="list-ul" id="playlists-ul">
+            {{#each playlists}}
+                <li>
+                    <a href="#edit_playlist_modal" class="edit-playlist-button" data-playlist={{this.name}}>
+                        {{this.displayName}}
+                        <a href="#" class="right select-playlist" data-playlist={{this.name}}>
+                            {{#if isPrivateRoomOwner name}}
+                                {{#if isPlaylistSelected name this.name}}
+                                    <i class="material-icons" style="line-height: 64px;">check_circle</i>
+                                {{else}}
+                                    <i class="material-icons" data-playlist={{this.name}} style="line-height: 64px;" title="Select playlist">panorama_fish_eye</i>
+                                {{/if}}
+                            {{/if}}
+                        </a>
+                    </a>
+                </li>
+            {{/each}}
+        </ul>
+        <div>
+            <a id="add-playlist-modal-button" class="waves-effect waves-light btn" href="#create_playlist_modal">Create Playlist</a>
+        </div>
+    </div>
+
+    <!--room controls-->
+    <ul id='control-dropdown' style="background-color: #00bfa5 !important; display: none">
+        {{#if playing}}
+            <li><a id="pause"><i class="material-icons">pause</i></a></li>
+        {{else}}
+            <li><a id="play"><i class="material-icons">play_arrow</i></a></li>
+        {{/if}}
         <li><a id="skip"><i class="material-icons">skip_next</i></a></li>
-        <li><a id="shuffle"><i class="material-icons">shuffle</i></a></li>
-        <li><a id="lock"><i class="material-icons">lock_outline</i></a></li>
+        {{#if private}}
+            <li><a id="unlock"><i class="material-icons">lock_outline</i></a></li>
+        {{else}}
+            <li><a id="lock"><i class="material-icons">lock_open</i></a></li>
+        {{/if}}
     </ul>
+
+
+    <!-- Edit Playlist Modal -->
+    <div id="edit_playlist_modal" class="modal">
+        <div class="modal-content">
+            <h4>Editing: {{editingPlaylist.name}}</h4>
+            {{#each song in editingPlaylist.songs}}
+                <a href="https://youtube.com/watch?v={{song.id}}" target="_blank">{{song.title}}</a> <span class="red-text remove_playlist_button" data-id={{song.id}} style="cursor: pointer">remove</span><br>
+            {{/each}}
+            <div class="input-field">
+                <input id="add_playlist_video" type="text">
+                <label for="add_playlist_video">Video ID</label>
+            </div>
+            <button class="btn waves-effect waves-light" id="add_playlist_video_submit">Add video</button>
+            <button class="btn waves-effect waves-light right red" id="delete_playlist">Delete playlist</button>
+        </div>
+        <div class="divider"></div>
+        <div class="modal-footer">
+            <a class="modal-action modal-close waves-effect btn">Close</a>
+        </div>
+    </div>
+    <!-- Create Playlist Modal -->
+    <div id="create_playlist_modal" class="modal">
+        <div class="modal-content">
+            <h4>Create Modal</h4>
+            <div class="input-field">
+                <input id="create_playlist_name" type="text">
+                <label for="create_playlist_name">Name</label>
+            </div>
+            <div class="input-field">
+                <input id="create_playlist_display_name" type="text">
+                <label for="create_playlist_display_name">Display Name</label>
+            </div>
+            <button class="btn waves-effect waves-light" id="create_playlist_submit">Create</button>
+        </div>
+        <div class="divider"></div>
+        <div class="modal-footer">
+            <a class="modal-action modal-close waves-effect btn">Close</a>
+        </div>
+    </div>
     <script>
+        $(".edit-playlist-button").leanModal({
+            dismissible: true,
+            opacity: .5,
+            in_duration: 500,
+            out_duration: 200
+        });
+        $("#add-playlist-modal-button").leanModal({
+            dismissible: true,
+            opacity: .5,
+            in_duration: 500,
+            out_duration: 200
+        });
         $(".dropdown-button").dropdown({
             belowOrigin: true
         });
@@ -112,6 +226,15 @@
             menuWidth: 350,
             edge: 'right'
         });
+
+        $("#allowed-slideout").sideNav({
+            menuWidth: 350,
+            edge: 'right'
+        });
+        $("#playlists-slideout").sideNav({
+            menuWidth: 350,
+            edge: 'right'
+        });
         $('.tooltipped').tooltip({delay: 50});
     </script>
 </template>

+ 5 - 0
app/lib/schemas.js

@@ -267,6 +267,11 @@ Schemas.Room = new SimpleSchema({
     "userList.$": {
         type: String,
         label: "Username of user currently in a room"
+    },
+    "playlist": {
+        type: String,
+        optional: true,
+        label: "Name of current playlist selected from owner"
     }
 });
 

+ 179 - 18
app/server/server.js

@@ -166,7 +166,7 @@ function createPrivateRoom(name, display, private, desc, owner) {
             displayName: display,
             private: private,
             roomDesc: desc,
-            owner: owner
+            owner: owner,
         }, function (err) {
             if (err) {
                 throw err;
@@ -530,13 +530,27 @@ function PrivateStation(name) {
         if (PrivatePlaylists.findOne({name: plName, owner: _room.owner}) !== undefined) {
             PrivateRooms.update({name: name}, {$set: {"playlist": plName}});
             _room.playlist = plName;
-            playlist = PrivatePlaylists({name: plName, owner: _room.owner});
+            playlist = PrivatePlaylists.findOne({name: plName, owner: _room.owner});
             songs = playlist.songs;
 
             currentSong = 0;
         }
     };
 
+    this.addAllowed = function (allowed) {
+        if (_room.allowed.indexOf(allowed) === -1 && _room.owner !== allowed) {
+            PrivateRooms.update({name: name}, {$push: {allowed: allowed}});
+            _room = PrivateRooms.findOne({name: name});
+        }
+    };
+
+    this.removeAllowed = function (allowed) {
+        if (_room.allowed.indexOf(allowed) !== -1) {
+            PrivateRooms.update({name: name}, {$pull: {allowed: allowed}});
+            _room = PrivateRooms.findOne({name: name});
+        }
+    };
+
     this.skipSong();
     this.voted = [];
 }
@@ -618,6 +632,28 @@ function getSongDuration(query, artistName) {
     return 0;
 }
 
+function getSongDataYT(id) {
+    var res = Meteor.http.get('https://www.googleapis.com/youtube/v3/videos?id=' + encodeURIComponent(id) + '&part=snippet,contentDetails&key=AIzaSyAgBdacEWrHCHVPPM4k-AFM7uXg-Q__YXY');
+    if (res.data !== undefined && res.data.items.length !== 0) {
+        var dur = res.data.items[0].contentDetails.duration;
+        dur = dur.replace("PT", "");
+        var durInSec = 0;
+        dur = dur.replace(/([\d]*)M/, function(v, v2) {
+            v2 = Number(v2);
+            durInSec = (v2 * 60)
+            return "";
+        });
+        dur = dur.replace(/([\d]*)S/, function(v, v2) {
+            v2 = Number(v2);
+            durInSec += v2;
+            return "";
+        });
+        return {duration: durInSec, title: res.data.items[0].snippet.title};
+    } else {
+        return 0;
+    }
+}
+
 function getSongAlbumArt(query, artistName) {
     var albumart;
     var search = query;
@@ -710,21 +746,20 @@ Meteor.publish("news", function () {
 
 Meteor.publish("userData", function (userId) {
     if (userId !== undefined) {
-        return Meteor.users.find(userId, {fields: {"services.github.username": 1, "punishments": 1}})
+        return Meteor.users.find(userId, {fields: {"services.github.username": 1, "punishments": 1}});
     } else {
         return undefined;
     }
 });
 
-Meteor.publish("playlists", function () {
-    return Playlists.find({})
+Meteor.publish("usernames", function () {
+    return Meteor.users.find({}, {fields: {"profile.username": 1}})
 });
 
-Meteor.publish("private_playlists", function () {
-    return PrivatePlaylists.find({})
+Meteor.publish("playlists", function () {
+    return Playlists.find({})
 });
 
-
 Meteor.publish("rooms", function () {
     return Rooms.find({});
 });
@@ -733,6 +768,14 @@ Meteor.publish("private_rooms", function () {
     return PrivateRooms.find({});
 });
 
+Meteor.publish("private_playlists", function () {
+    if (this.userId !== undefined) {
+        return PrivatePlaylists.find({owner: this.userId});
+    } else {
+        return null;
+    }
+});
+
 Meteor.publish("songs", function () {
     return Songs.find({});
 });
@@ -793,9 +836,8 @@ Meteor.publish("feedback", function(){
 })
 
 function isPrivateRoomOwner(name) {
-    var userData = Meteor.users.find(Meteor.userId());
     var room = PrivateRooms.findOne({name: name});
-    if (Meteor.userId() && userData.count !== 0 && room !== undefined && userData.fetch()[0].profile.username === room.owner) {
+    if (Meteor.userId() && room !== undefined && Meteor.userId() === room.owner) {
         return true;
     } else {
         return false;
@@ -851,6 +893,68 @@ function isMuted() {
 }
 
 Meteor.methods({
+    addVideoToPrivatePlaylist: function(name, id) {
+        if (Meteor.userId() && !isBanned()) {
+            var pl = PrivatePlaylists.findOne({owner: Meteor.userId(), name: name});
+            if (pl !== undefined) {
+                var copy = false;
+                pl.songs.forEach(function(song) {
+                    if (song.id === id) {
+                        copy = true;
+                    }
+                });
+                if (!copy) {
+                    console.log(getSongDataYT(id));
+                    var data = getSongDataYT(id);
+                    data.id = id;
+                    PrivatePlaylists.update({owner: Meteor.userId(), name: name}, {$push: {songs: data}});
+                } else {
+                    throw new Meteor.Error(500, "Video is already in playlist.");
+                }
+            } else {
+                throw new Meteor.Error(404, "Playlist not found.");
+            }
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
+    removeVideoFromPrivatePlaylist: function(name, id) {
+        if (Meteor.userId() && !isBanned()) {
+            var pl = PrivatePlaylists.findOne({owner: Meteor.userId(), name: name});
+            if (pl !== undefined) {
+                var copy = false;
+                var data;
+                pl.songs.forEach(function(song) {
+                    if (song.id === id) {
+                        data = song;
+                        copy = true;
+                    }
+                });
+                if (copy) {
+                    PrivatePlaylists.update({owner: Meteor.userId(), name: name}, {$pull: {songs: data}});
+                } else {
+                    throw new Meteor.Error(500, "Video is not in playlist.");
+                }
+            } else {
+                throw new Meteor.Error(404, "Playlist not found.");
+            }
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
+    deletePrivatePlaylist: function(name) {
+        if (Meteor.userId() && !isBanned()) {
+            var pl = PrivatePlaylists.findOne({owner: Meteor.userId(), name: name});
+            if (pl !== undefined) {
+                PrivatePlaylists.remove({owner: Meteor.userId(), name: name});
+                Deleted.insert({type: "PrivatePlaylist", data: pl});
+            } else {
+                throw new Meteor.Error(404, "Playlist not found.");
+            }
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
     activateAlert: function(id) {
         if (isAdmin() && !isBanned()) {
             Alerts.update(id, {$set: {active: true}});
@@ -909,7 +1013,7 @@ Meteor.methods({
         }
     },
     lockPrivateRoom: function (name) {
-        if ((isAdmin() || isPrivateRoomOwner()) && !isBanned()) {
+        if ((isAdmin() || isPrivateRoomOwner(name)) && !isBanned()) {
             getPrivateStation(name, function (station) {
                 station.lock();
             });
@@ -918,7 +1022,7 @@ Meteor.methods({
         }
     },
     unlockPrivateRoom: function (name) {
-        if ((isAdmin() || isPrivateRoomOwner) && !isBanned()) {
+        if ((isAdmin() || isPrivateRoomOwner(name)) && !isBanned()) {
             getPrivateStation(name, function (station) {
                 station.unlock();
             });
@@ -1135,6 +1239,22 @@ Meteor.methods({
             })
         }
     },
+    votePrivateSkip: function (name) {
+        if (Meteor.userId() && !isBanned()) {
+            var user = Meteor.user();
+            getPrivateStation(name, function (station) {
+                if (station.voted.indexOf(user.profile.username) === -1) {
+                    station.voted.push(user.profile.username);
+                    PrivateRooms.update({name: name}, {$set: {votes: station.voted.length}});
+                    if (station.voted.length === 3) {
+                        station.skipSong();
+                    }
+                } else {
+                    throw new Meteor.Error(401, "Already voted.");
+                }
+            })
+        }
+    },
     submitReport: function (room, reportData) {
         if (Meteor.userId() && !isBanned()) {
             room = room.toLowerCase();
@@ -1214,7 +1334,7 @@ Meteor.methods({
         }
     },
     skipPrivateSong: function (name) {
-        if ((isAdmin() || isRoomOwner()) && !isBanned()) {
+        if ((isAdmin() || isPrivateRoomOwner(name)) && !isBanned()) {
             getPrivateStation(name, function (station) {
                 if (station === undefined) {
                     throw new Meteor.Error(404, "Station not found.");
@@ -1225,7 +1345,7 @@ Meteor.methods({
         }
     },
     pausePrivateRoom: function (name) {
-        if ((isAdmin() || isPrivateRoomOwner()) && !isBanned()) {
+        if ((isAdmin() || isPrivateRoomOwner(name)) && !isBanned()) {
             getPrivateStation(name, function (station) {
                 if (station === undefined) {
                     throw new Meteor.Error(403, "Room doesn't exist.");
@@ -1238,7 +1358,7 @@ Meteor.methods({
         }
     },
     resumePrivateRoom: function (name) {
-        if ((isAdmin() || isPrivateRoomOwner()) && !isBanned()) {
+        if ((isAdmin() || isPrivateRoomOwner(name)) && !isBanned()) {
             getPrivateStation(name, function (station) {
                 if (station === undefined) {
                     throw new Meteor.Error(403, "Room doesn't exist.");
@@ -1250,6 +1370,47 @@ Meteor.methods({
             throw new Meteor.Error(403, "Invalid permissions.");
         }
     },
+    addAllowedToPrivateRoom: function (name, allowed) {
+        if (isPrivateRoomOwner(name) && !isBanned()) {
+            getPrivateStation(name, function (station) {
+                if (station === undefined) {
+                    throw new Meteor.Error(403, "Room doesn't exist.");
+                } else {
+                    allowed = allowed.toLowerCase();
+                    allowed = Meteor.users.findOne({"profile.usernameL": allowed})._id;
+                    station.addAllowed(allowed);
+                }
+            });
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
+    removeAllowedFromPrivateRoom: function (name, allowed) {
+        if (isPrivateRoomOwner(name) && !isBanned()) {
+            getPrivateStation(name, function (station) {
+                if (station === undefined) {
+                    throw new Meteor.Error(403, "Room doesn't exist.");
+                } else {
+                    station.removeAllowed(allowed);
+                }
+            });
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
+    setPlaylistForPrivateRoom: function (name, playlist) {
+        if ((isPrivateRoomOwner(name)) && !isBanned()) {
+            getPrivateStation(name, function (station) {
+                if (station === undefined) {
+                    throw new Meteor.Error(403, "Room doesn't exist.");
+                } else {
+                    station.setPlaylist(playlist);
+                }
+            });
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
     createUserMethod: function (formData, captchaData) {
         if (!isBanned()) {
             var verifyCaptchaResponse = reCAPTCHA.verifyCaptcha(this.connection.clientAddress, captchaData);
@@ -1449,15 +1610,15 @@ Meteor.methods({
     },
     createPrivateRoom: function (name, display, private, desc) {
         if (Meteor.userId() && !isBanned()) {
-            createPrivateRoom(name, display, private, desc, Meteor.user().profile.username);
+            createPrivateRoom(name, display, private, desc, Meteor.userId());
         } else {
             throw new Meteor.Error(403, "Invalid permissions.");
         }
     },
     createPrivatePlaylist: function (name, display) {
         if (Meteor.userId() && !isBanned()) {
-            if (PrivatePlaylists.findOne({name: name, owner: Meteor.user().profile.username}) === undefined) {
-                PrivatePlaylists.insert({name: name, displayName: display, songs: [{id: "60ItHLz5WEA", duration: 213, title: "Alan Walker - Faded"}], owner: Meteor.user().profile.username});
+            if (PrivatePlaylists.findOne({name: name, owner: Meteor.userId()}) === undefined) {
+                PrivatePlaylists.insert({name: name, displayName: display, songs: [{id: "60ItHLz5WEA", duration: 213, title: "Alan Walker - Faded"}], owner: Meteor.userId()});
             }
         } else {
             throw new Meteor.Error(403, "Invalid permissions.");