mondora/mondora-website-front

View on GitHub
app/startup.js

Summary

Maintainability
D
2 days
Test Coverage
var GIVE_UP_DELAY = 30000;

(function () {

    var deferred = Q.defer();

    window.Ceres = new Asteroid(
        window.APP_CONFIG.BACKEND_HOST || "localhost:3000",
        window.APP_CONFIG.BACKEND_USE_SSL || false
    );
    Ceres.on("connected", function () {
        deferred.resolve();
    });
    Ceres.ddp.on("socket_close", function () {
        console.log("Closed");
    });

    window.CERES_CONNECTED = deferred.promise.timeout(GIVE_UP_DELAY);

})();

angular.module("mnd-web")

.factory("TimeoutPromiseService", ["$q", "$timeout", "$state", function ($q, $timeout, $state) {
    var timeoutPromise = function (promise, t) {
        var deferred = $q.defer();
        var timer = $timeout(function () {
            deferred.reject("timeout");
            $state.go("serverProblems");
        }, t);
        promise.then(function (res) {
            $timeout.cancel(timer);
            deferred.resolve(res);
        }, function (err) {
            $timeout.cancel(timer);
            deferred.reject(err);
            $state.go("serverProblems");
        });
        return deferred.promise;
    };
    return {
        timeoutPromise: timeoutPromise
    };
}])

.config(["$locationProvider", function ($locationProvider) {
    $locationProvider.hashPrefix("!");
}])

.config(["uiSelectConfig", function (uiSelectConfig) {
    uiSelectConfig.theme = "bootstrap";
}])

.config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) {



    //////////////
    // SEO tags //
    //////////////

    var setSeoTags = function (values) {

        // Description
        var descriptionNodesSelector = [
            "#meta-description",
            "#meta-gp-description",
            "#meta-tw-description",
            "#meta-fb-description"
        ].join(",");
        var descriptionNodes = angular.element(document.querySelectorAll(descriptionNodesSelector));
        descriptionNodes.attr({
            content: values.description
        });

        // Title
        var titleNodesSelector = [
            "#meta-gp-title",
            "#meta-tw-title",
            "#meta-fb-title"
        ].join(",");
        var titleNodes = angular.element(document.querySelectorAll(titleNodesSelector));
        titleNodes.attr({
            content: values.title
        });
        // Set the page title
        angular.element(document.querySelector("title")).html(values.title);

        // Image
        var imageNodesSelector = [
            "#meta-gp-image",
            "#meta-tw-image",
            "#meta-fb-image"
        ].join(",");
        var imageNodes = angular.element(document.querySelectorAll(imageNodesSelector));
        imageNodes.attr({
            content: values.image
        });

        // Type
        var html = angular.element(document.querySelector("html"));
        html.attr({
            itemtype: values.itemtype
        });
        var typeNodesSelector = [
            "#meta-fb-type"
        ].join(",");
        var typeNodes = angular.element(document.querySelector(typeNodesSelector));
        typeNodes.attr({
            content: values.type
        });

    };

    var resetSeoTags = function () {
        setSeoTags({
            description: "mondora app",
            title: "mondora :m",
            image: "https://mnd-website.s3.amazonaws.com/img/mondora-logo.png",
            type: "website",
            itemtype: "https://schema.org/Organization"
        });
    };



    /////////////////////////
    // Root abstract state //
    /////////////////////////

    $stateProvider.state("root", {
        abstract: true,
        templateUrl: "root.html",
        resolve: {
            resumingLogin: ["TimeoutPromiseService", "$state", function (TimeoutPromiseService, $state) {
                return CERES_CONNECTED
                    .then(function () {
                        var resProm = Ceres.resumeLoginPromise;
                        if (resProm.isPending()) {
                            return resProm
                                .timeout(GIVE_UP_DELAY)
                                .fail(function () {
                                    return true;
                                });
                        }
                        return true;
                    })
                    .then(function () {
                        var sub = Ceres.subscribe("configurations");
                        return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
                    })
                    .fail(function (e) {
                        $state.go("staticHome");
                    });
            }]
        }
    });



    ///////////////////////
    // Pre-defined pages //
    ///////////////////////

    $stateProvider.state("home", {
        url: "/",
        parent: "root",
        templateUrl: "pages/home/home.html",
        controller: "HomeController",
        onEnter: ["$rootScope", "$state", function ($rootScope, $state) {
            if ($rootScope.user) {
                $state.go("personalHome");
            }
        }],
        resolve: {
            homepagePostsSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("latestPosts", 3, true);
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }],
            stravaActivitiesSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("stravaLatestActivities", 7);
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        },
        public: true
    });

    $stateProvider.state("staticHome", {
        url: "/staticHome",
        templateUrl: "pages/staticHome/staticHome.html",
        controller: "StaticHomeController",
        public: true
    });

    $stateProvider.state("notFound", {
        url: "/notFound",
        parent: "root",
        templateUrl: "pages/notFound/notFound.html",
        public: true
    });

    $stateProvider.state("serverProblems", {
        url: "/serverProblems",
        parent: "root",
        templateUrl: "pages/serverProblems/serverProblems.html",
        public: true
    });



    //////////
    // Apps //
    //////////
    // TODO make this dynamic

    $stateProvider.state("personalHome", {
        url: "/home",
        parent: "root",
        templateUrl: "pages/personalHome/personalHome.html",
        controller: "PersonalHomeController"
    });

    $stateProvider.state("profile", {
        url: "/profile",
        parent: "root",
        templateUrl: "pages/profile/profile.html",
        controller: "ProfileController"
    });

    $stateProvider.state("admin", {
        url: "/admin",
        parent: "root",
        templateUrl: "pages/admin/admin.html",
        controller: "AdminController",
        resolve: {
            serverConfigurationsSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("serverConfigurations");
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });

    $stateProvider.state("users", {
        url: "/users",
        parent: "root",
        templateUrl: "pages/users/users.html",
        controller: "UsersController",
        resolve: {
            usersAdminSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("usersAdmin");
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });

    $stateProvider.state("today", {
        url: "/today",
        parent: "root",
        templateUrl: "pages/today/today.html",
        controller: "TodayController",
        resolve: {
            pomoSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("tasks");
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });

    $stateProvider.state("calendar", {
        url: "/calendar",
        parent: "root",
        templateUrl: "pages/calendar/calendar.html",
        controller: "CalendarController"
    });

    $stateProvider.state("projects", {
        url: "/projects",
        parent: "root",
        templateUrl: "pages/projects/projects.html",
        controller: "ProjectsController",
        resolve: {
            projectsSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("allProjects");
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });

    $stateProvider.state("inbox", {
        url: "/inbox",
        parent: "root",
        templateUrl: "pages/inbox/inbox.html",
        controller: "InboxController"
    });

    $stateProvider.state("verifyEmail", {
        url: "/verifyEmail/:token",
        parent: "root",
        templateUrl: "pages/verifyEmail/verifyEmail.html",
        controller: "VerifyEmailController"
    });



    ////////////////////
    // Users and team //
    ////////////////////

    $stateProvider.state("user", {
        url: "/user/:userId",
        parent: "root",
        templateUrl: "pages/user/user.html",
        controller: "UserController",
        resolve: {
            userSub: ["$stateParams", function ($stateParams) {
                return Ceres.subscribe("singleUser", $stateParams.userId);
            }],
            postsByAuthorSub: ["$stateParams", function ($stateParams) {
                return Ceres.subscribe("postsByAuthor", $stateParams.userId);
            }]
        },
        onEnter: ["$state", "$stateParams", "$rootScope", "MndTagStrippingService", function ($state, $stateParams, $rootScope, MndTagStrippingService) {
            var user = $rootScope.Users.reactiveQuery({_id: $stateParams.userId}).result[0];
            if (!user) {
                $state.go("notFound");
                return;
            }
            setSeoTags({
                description: MndTagStrippingService(user.profile.bio).slice(0, 150),
                title: "mondora :m - " + user.profile.name,
                image: user.profile.pictureUrl,
                type: "person",
                itemtype: "https://schema.org/Person"
            });
        }],
        onExit: [function () {
            resetSeoTags();
        }],
        public: true
    });

    $stateProvider.state("team", {
        url: "/team",
        parent: "root",
        templateUrl: "pages/team/team.html",
        controller: "TeamController",
        resolve: {
            teamSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("teamUsers");
                sub.ready.then(function () {
                    console.log(sub);
                });
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        },
        public: true
    });

    $stateProvider.state("aboutUs", {
       url: "/aboutUs",
        parent: "root",
        templateUrl: "pages/aboutUs/aboutUs.html"
    });


    //////////
    // Post //
    //////////

    $stateProvider.state("postList", {
        url: "/post/list",
        parent: "root",
        templateUrl: "pages/post/list/postList.html",
        controller: "PostListController",
        resolve: {
            latestPostsSub: ["TimeoutPromiseService", function (TimeoutPromiseService) {
                var sub = Ceres.subscribe("latestPosts");
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        },
        public: true
    });

    $stateProvider.state("postView", {
        url: "/post/:postId",
        parent: "root",
        templateUrl: "pages/post/view/postView.html",
        controller: "PostViewController",
        resolve: {
            postSubId: ["$stateParams", "TimeoutPromiseService", "resumingLogin", function ($stateParams, TimeoutPromiseService, resumingLogin) {
                var sub = Ceres.subscribe("singlePost", $stateParams.postId);
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        },
        onEnter: ["$state", "$stateParams", "$rootScope", "MndTagStrippingService", function ($state, $stateParams, $rootScope, MndTagStrippingService) {
            var post = $rootScope.Posts.reactiveQuery({_id: $stateParams.postId}).result[0];
            if (!post) {
                $state.go("notFound");
                return;
            }
            setSeoTags({
                description: MndTagStrippingService(post.body).slice(0, 150),
                title: "mondora :m - " + post.title,
                image: post.titleImageUrl,
                type: "article",
                itemtype: "https://schema.org/Article"
            });
        }],
        onExit: [function () {
            resetSeoTags();
        }],
        public: true
    });

    $stateProvider.state("postEdit", {
        url: "/post/:postId/edit",
        parent: "root",
        templateUrl: "pages/post/edit/postEdit.html",
        controller: "PostEditController",
        resolve: {
            postSubId: ["$stateParams", "TimeoutPromiseService", "resumingLogin", function ($stateParams, TimeoutPromiseService, resumingLogin) {
                var sub = Ceres.subscribe("singlePost", $stateParams.postId);
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });

    $stateProvider.state("topic", {
        url: "/topic/:name",
        parent: "root",
        templateUrl: "pages/topic/topic.html",
        controller: "TopicController",
        resolve: {
            topic: ["TimeoutPromiseService", "$stateParams", function (TimeoutPromiseService, $stateParams) {
                var meth = Ceres.call("getTopic", $stateParams.name);
                return TimeoutPromiseService.timeoutPromise(meth.result, GIVE_UP_DELAY);
            }]
        },
        public: true
    });



    /////////////
    // Channel //
    /////////////

    $stateProvider.state("channelList", {
        url: "/channel/list",
        parent: "root",
        templateUrl: "pages/channel/list/channelList.html",
        controller: "ChannelListController",
        public: true
    });

    $stateProvider.state("channelView", {
        url: "/channel/:channelNameOrId",
        parent: "root",
        templateUrl: "pages/channel/view/channelView.html",
        controller: "ChannelViewController",
        resolve: {
            channelSubId: ["$stateParams", "TimeoutPromiseService", "resumingLogin", function ($stateParams, TimeoutPromiseService, resumingLogin) {
                var sub = Ceres.subscribe("singleChannel", $stateParams.channelNameOrId);
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        },
        public: true
    });

    $stateProvider.state("channelEdit", {
        url: "/channel/:channelNameOrId/edit",
        parent: "root",
        templateUrl: "pages/channel/edit/channelEdit.html",
        controller: "ChannelEditController",
        resolve: {
            channelSubId: ["$stateParams", "TimeoutPromiseService", "resumingLogin", function ($stateParams, TimeoutPromiseService, resumingLogin) {
                var sub = Ceres.subscribe("singleChannel", $stateParams.channelNameOrId);
                sub.ready.fail(function (e) {console.log(e);});
                return TimeoutPromiseService.timeoutPromise(sub.ready, GIVE_UP_DELAY);
            }]
        }
    });



    ///////////////
    // Otherwise //
    ///////////////

    $urlRouterProvider.otherwise("/");

}])



.run(["$rootScope", "$state", "$interval", "MndSidebarService", function ($rootScope, $state, $interval, MndSidebarService) {

    // TODO: REFACTOR
    $rootScope.safeApply = function (fn) {
        var phase = $rootScope.$$phase;
        if (phase === "$apply" || phase === "$digest") {
            if (typeof fn === "function") {
                fn();
            }
        } else {
            this.$apply(fn);
        }
    };

    $rootScope.Ceres = Ceres;
    Ceres.subscribe("userAdditionalInfo");
    Ceres.subscribe("allUsers");
    Ceres.subscribe("bookmarks");
    $rootScope.Configurations = Ceres.getCollection("configurations");
    $rootScope.Posts = Ceres.getCollection("posts");
    $rootScope.Channels = Ceres.getCollection("channels");
    $rootScope.Notifications = Ceres.getCollection("notifications");
    $rootScope.Users = Ceres.getCollection("users");
    $rootScope.StravaActivities = Ceres.getCollection("stravaActivities");

    Ceres.on("login", function (userId) {
        $rootScope.loggedInUserQuery = $rootScope.Users.reactiveQuery({_id: userId});
        var updateUser = function () {
            $rootScope.safeApply(function () {
                $rootScope.user = $rootScope.loggedInUserQuery.result[0];
                var notificationSubs = $rootScope.user && $rootScope.user.notificationChannelSubscriptions;
                if (Array.isArray(notificationSubs)) {
                    notificationSubs.forEach(function (notificationChannelName) {
                        Ceres.subscribe("notificationChannel", notificationChannelName);
                    });
                }
            });
        };
        $rootScope.loggedInUserQuery.on("change", updateUser);
        updateUser();
        if ($state.current.name === "home") {
            $state.go("personalHome");
        }
    });
    Ceres.on("logout", function () {
        $rootScope.safeApply(function () {
            delete $rootScope.user;
            if (!$state.current.public) {
                $state.go("home");
            }
        });
    });

    $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
        if (MndSidebarService.getSidebarStatus()) {
            MndSidebarService.toggleSidebarStatus();
        }
        $rootScope.$broadcast("sidebarStatusChanged");
        if (toState.name === "home" && $rootScope.user) {
            event.preventDefault();
            $state.go("personalHome");
        }
    });

    // Utility functions and properties
    $rootScope.guid = function () {
        return md5(Math.random().toString());
    };

    /////////////////
    // Server time //
    /////////////////

    $rootScope.serverTimeDelta = 0;
    $rootScope.serverTime = Date.now();

    var update = function () {
        if (Ceres.ddp.readyState !== 1) return;
        var outLatencyTimestamp = Date.now();
        Ceres.call("getServerTime").result.then(function (serverTime) {
            var inLatencyTimestamp = Date.now();
            var distance = (inLatencyTimestamp - outLatencyTimestamp) / 2;
            $rootScope.safeApply(function () {
                $rootScope.serverTime = serverTime;
                $rootScope.serverTimeDelta = inLatencyTimestamp - (serverTime + distance);
            });
        });
    };
    CERES_CONNECTED.then(function () {
        update();
        $interval(update, 10000, 0, false);
    });

}])



.run(["$rootScope", "$http", function ($rootScope, $http) {

    /////////////////////
    // Get app version //
    /////////////////////

    $http.get("/VERSION")
        .success(function (version) {
            if (localStorage.APP_VERSION !== version) {
                localStorage.APP_VERSION = version;
                window.location.reload(true);
            }
            $rootScope.APP_VERSION = version;
        });

}])



.controller("MainController", ["$scope", function ($scope) {
    $scope.login = $scope.loginWithTwitter = function () {
        $scope.Ceres.loginWithTwitter();
    };
    $scope.loginWithGoogle = function () {
        $scope.Ceres.loginWithGoogle();
    };
    $scope.logout = function () {
        $scope.Ceres.logout();
    };
}]);