superdesk/superdesk-client-core

View on GitHub
scripts/apps/monitoring/directives/MonitoringView.ts

Summary

Maintainability
D
2 days
Test Coverage
import _ from 'lodash';
import {gettext} from 'core/utils';
import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService';
import {IDesk, ISuperdeskQuery, IUser} from 'superdesk-api';

const PAGE_SIZE = 50;

interface IScope extends ng.IScope {
    contentStyle: {};
    monitoringItemsLoading: boolean;
    activeDeskId: IDesk['_id'] | null;
    swimlane: any;
    monitoring: any;
    numberOfColumns: number;
    desks: any;
    workspaces: any;
    view: string;
    workspace: any;
    shouldRefresh: boolean;
    type: any;
    group: any;
    refreshGroup(group?: any, triggeredByScrolling?: boolean);
    isActiveGroup: (group: any) => boolean;
    switchView: (value: any, swimlane?: any) => void;
    gettext: (text: any, params?: any) => any;
    toggleFilter: () => void;
    addResourceUpdatedEventListener: (callback: any) => void;
    openUpload: (files: Array<File>) => void;
    spikedItemsQuery: ISuperdeskQuery;
    highlightedItemsQuery: ISuperdeskQuery;
    selectedHighlightId: string;
    getExtraButtonsForHighlightsView(): Array<{label: string; onClick: () => void}>;
    afterWorkspaceRename: () => void;
    initWorkspaceRename: (workspace) => void;
    workspaceToRename: any;
}

/**
 * Main monitoring view - list + preview
 *
 * it's a directive so that it can be put together with authoring into some container directive
 */
MonitoringView.$inject = [
    '$rootScope',
    'authoringWorkspace',
    'pageTitle',
    '$timeout',
    '$location',
    'workspaces',
    'desks',
    'superdesk',
    'session',
    'privileges',
    'highlightsService',
];

export function MonitoringView(
    $rootScope,
    authoringWorkspace: AuthoringWorkspaceService,
    pageTitle,
    $timeout,
    $location,
    workspaces,
    desks,
    superdesk,
    session,
    privileges,
    highlightsService,
) {
    return {
        templateUrl: 'scripts/apps/monitoring/views/monitoring-view.html',
        controller: 'Monitoring',
        controllerAs: 'monitoring',
        scope: {
            type: '=',
            state: '=',
            customDataSource: '=?',
            onMonitoringItemSelect: '=?',
            onMonitoringItemDoubleClick: '=?',
            hideActionsForMonitoringItems: '=?',
            disableMonitoringMultiSelect: '=?',
            disableMonitoringCreateItem: '=?',
            hideMonitoringToolbar1: '=?',
            hideMonitoringToolbar2: '=?',
            selectedHighlightName: '=?',
            selectedHighlightId: '=?',
            contentStyle: '=?',
        },
        link: function(scope: IScope, elem) {
            let containerElem = elem.find('.sd-column-box__main-column');

            scope.contentStyle = scope.contentStyle ?? {padding: '0 20px 20px'};

            scope.gettext = gettext;

            scope.initWorkspaceRename = (workspace) => {
                scope.workspaceToRename = workspace;

                scope.afterWorkspaceRename = () => {
                    scope.workspaceToRename = undefined;
                };
            };

            scope.addResourceUpdatedEventListener = (callback) => {
                scope.$on('resource:updated', (_event, data) => {
                    callback(data);
                });
            };

            scope.openUpload = (files: Array<File>) => {
                superdesk.intent('upload', 'media', {
                    files: files,
                    deskSelectionAllowed: true,
                });
            };

            /**
             * Issue here is that sd-column-box__main-column element is not visible on initializing sd-monitoring-view.
             * So I added $broadcast and listener for updating onScroll binding and containerElem for it.
            */
            $rootScope.$on('stage:single', () => {
                containerElem = elem.find('.sd-column-box__main-column');
                containerElem.on('scroll', handleContainerScroll);
            });

            pageTitle.setUrl(_.capitalize(gettext(scope.type)));

            scope.shouldRefresh = true;
            scope.view = 'compact'; // default view

            scope.workspaces = workspaces;
            scope.$watch('workspaces.active', (workspace) => {
                scope.workspace = workspace;
            });
            workspaces.getActive();

            scope.desks = desks;
            scope.$watch(desks.getCurrentDesk.bind(desks), (currentDesk: IDesk | null) => {
                scope.activeDeskId = currentDesk?._id ?? null;

                if (scope.activeDeskId != null) {
                    session.getIdentity().then((user: IUser) => {
                        scope.spikedItemsQuery = {
                            filter: {$and: [
                                {'state': {$eq: 'spiked'}},
                                {$or: [
                                    {$and: [
                                        {'state': {$eq: 'draft'}},
                                        {'original_creator': {$eq: user._id}},
                                    ]},
                                    {'state': {$ne: 'draft'}},
                                ]},
                                {'package_type': {$ne: 'takes'}},
                                {'task.desk': {$eq: scope.activeDeskId}},
                            ]},
                            sort: [{'versioncreated': 'desc'}],
                            page: 1,
                            max_results: PAGE_SIZE,
                        };

                        scope.highlightedItemsQuery = {
                            filter: {$and: [
                                {'state': {$ne: 'spiked'}},
                                {$or: [
                                    {$and: [
                                        {'state': {$eq: 'draft'}},
                                        {'original_creator': {$eq: user._id}},
                                    ]},
                                    {'state': {$ne: 'draft'}},
                                ]},
                                {'package_type': {$ne: 'takes'}},
                                {'highlights': {$eq: $location.search().highlight}},
                            ]},
                            sort: [{'versioncreated': 'desc'}],
                            page: 1,
                            max_results: PAGE_SIZE,
                        };

                        scope.getExtraButtonsForHighlightsView = () => {
                            if (privileges.privileges.mark_for_highlights) {
                                return [
                                    {
                                        label: gettext('Create'),
                                        onClick: () => {
                                            highlightsService.find(scope.selectedHighlightId)
                                                .then(highlightsService.createEmptyHighlight)
                                                .then(authoringWorkspace.edit);
                                        },
                                    },
                                ];
                            } else {
                                return null;
                            }
                        };
                    });
                }

                if (currentDesk && currentDesk.monitoring_default_view) {
                    switch (currentDesk.monitoring_default_view) {
                    case 'list':
                        scope.switchView('compact');
                        break;
                    case 'swimlane':
                        scope.switchView('compact', true);
                        break;
                    case 'photogrid':
                        scope.switchView('photogrid');
                        break;
                    default:
                        scope.switchView('compact'); // list by default
                        break;
                    }
                }
            });

            scope.numberOfColumns = 1;

            /**
             * Toggle viewColumn to switch views between swimlane and list
             * @param {Boolean} value
             * @param {Boolean} swimlane
             */
            scope.switchView = function(value, swimlane) {
                scope.monitoring.switchViewColumn(swimlane, true);
                scope.view = value;
                scope.swimlane = swimlane;
            };

            /**
             * @description Returns true when group's item is selected for previewing, false otherwise.
             * @param {Object} group
             * @returns {Boolean}
             */
            scope.isActiveGroup = function(group) {
                return scope.monitoring.selectedGroup ? scope.monitoring.selectedGroup._id === group._id : true;
            };

            var updateTimeout;

            function handleContainerScroll($event) {
                if ($rootScope.itemToggle) {
                    scope.$applyAsync(() => {
                        $rootScope.itemToggle(false);
                        $rootScope.itemToggle = null;
                    });
                }

                if (scope.monitoring.viewColumn && containerElem[0].scrollTop === 0) {
                    scope.refreshGroup(scope.group, true);
                }

                $timeout.cancel(updateTimeout);
                updateTimeout = $timeout(renderIfNeeded, 100, false);
            }

            containerElem.on('scroll', handleContainerScroll);

            function isListEnd(container) {
                return container.scrollTop + container.offsetHeight + 200 >= container.scrollHeight;
            }

            /**
             * Trigger render in case user scrolls to the very end of list
             */
            function renderIfNeeded() {
                if (isListEnd(containerElem[0])) {
                    scheduleFetchNext();
                }
            }

            let fetchNextTimeout;

            /**
             * Schedule content fetchNext after some delay
             */
            function scheduleFetchNext() {
                if (!fetchNextTimeout) {
                    fetchNextTimeout = $timeout(() => {
                        scope.$broadcast('render:next');
                        scope.$applyAsync(() => {
                            fetchNextTimeout = null;
                        });
                    }, 1000, false);
                }
            }

            // force refresh on refresh button click when in specific view such as single, highlights or spiked.
            scope.refreshGroup = function(group, triggeredByScrolling?: boolean) {
                scope.$broadcast('refresh:list', group, triggeredByScrolling ? {event_origin: 'scroll'} : undefined);
            };

            scope.$on('$destroy', () => {
                containerElem.off();
            });

            scope.$on('$routeUpdate', (event, data) => {
                if (scope.shouldRefresh) {
                    scope.refreshGroup();
                } else {
                    scope.shouldRefresh = true;
                }
            });

            scope.$watch(() => authoringWorkspace.item, (newValue, oldValue) => {
                if (newValue !== oldValue) {
                    scope.shouldRefresh = false; // when item opened or closed
                    if (newValue) { // when item opened
                        scope.monitoring.closePreview();
                    }
                }
            });
        },
    };
}