src/modules/sepehr/sepehr.module.ts

Summary

Maintainability
C
1 day
Test Coverage
import * as moment from 'moment-jalaali';
import { Module } from '../../libs';
import template from './sepehr.template.html';
import categoriesTemplate from './sepehr-categories.template.html';
import channelsTemplate from './sepehr-channels.template.html';
import epgTemplate from './sepehr-epg.template.html';
import { SepehrService } from './sepehr.service';
import { DefaultResponse } from '../../_models';
import { SepehrCategories, SepehrChannel, SepehrEpg } from './sepehr.models';
import * as $ from 'jquery';

export default class SepehrModule extends Module {

    private readonly currentDate;
    protected events = {
        'sepehr-categories.prev': { control: 'right', title: 'قبلی', icon: 'right' },
        'sepehr-categories.next': { control: 'left', title: 'بعدی', icon: 'left' },
        'sepehr-categories.enter': { control: 'enter', title: 'انتخاب', icon: 'enter' },
        'sepehr-categories.back': { control: 'back,backspace', title: 'بازگشت', icon: 'refresh' },

        'sepehr-channels.prev': { control: 'up', title: 'قبلی', icon: 'up' },
        'sepehr-channels.next': { control: 'down', title: 'بعدی', icon: 'bottom' },
        'sepehr-channels.back': { control: 'back,backspace', title: 'بازگشت', icon: 'refresh' },

        'sepehr-epg.prev': { control: 'up', title: 'برنامه قبلی', icon: 'up' },
        'sepehr-epg.next': { control: 'down', title: 'برنامه بعدی', icon: 'bottom' },
        'sepehr-epg.back': { control: 'back,backspace', title: 'بازگشت', icon: 'refresh' },

        'sepehr-live.enter': { control: 'blue,b', title: 'پخش زنده', icon: 'square', button: true },
        'sepehr-epg.enter': { control: 'green,g', title: ' جدول پخش', icon: 'square', button: true },
    };

    constructor(config: any = {}, layoutInstance?) {
        super(config, layoutInstance);

        moment.locale('en');
        this.currentDate = moment().format('YYYY-MM-DD');
        this.service = SepehrService.instance;
        this.events = this.prepareControls();

        this.loadCategories();
        // this.render();
        this.templateHelper.loading();

        return this;
    }

    public render(callback?: (() => any)): void {
        this.templateHelper.render(template, {}, this.$el, 'html');
    }

    // Categories

    private loadCategories(): void {
        const self = this;
        this.service.getCategories().done((response: DefaultResponse<SepehrCategories[]>) => {
            this.templateHelper.render(categoriesTemplate, response.data, this.$el, 'html');
            self.initializeCategoriesSlider();
            self.templateHelper.loading(false);
        });
    }

    private initializeCategoriesSlider(): void {
        const self = this;
        const $el = $("ul.sepehr-categories");
        if (!$el.is(':visible'))
            $el.show(1);
        $el.on('afterChange', () => {
            self.input.removeEvent('enter', self.events['sepehr-categories.enter']);
            setTimeout(() => {
                self.input.addEvent('enter', false, self.events['sepehr-categories.enter'], () => {
                    self.loadCategoryChannels($el);
                });
            }, 100);
        });
        $el.slick({
            rtl: $("body").hasClass('rtl'),
            slidesToShow: 3,
            slidesToScroll: 1,
            centerMode: true,
        });
        $el.slick('slickGoTo', 0, true);
        this.registerCategoriesKeyboardInputs($el);
    }

    private loadCategoryChannels($list): void {
        const categoryId = $list.find('.slick-current li').data('id');
        this.loadChannelsByCategoryId(categoryId);
    }

    private registerCategoriesKeyboardInputs($carousel = $("ul.sepehr-categories")): void {
        this.input.addEvent('right', false, this.events['sepehr-categories.prev'], () => {
            $carousel.slick('slickPrev');
        });
        this.input.addEvent('left', false, this.events['sepehr-categories.next'], () => {
            $carousel.slick('slickNext');
        });
    }

    private removeCategoriesKeyboardInputs(): void {
        this.input.removeEvent('right', this.events['sepehr-categories.prev']);
        this.input.removeEvent('left', this.events['sepehr-categories.next']);
        this.input.removeEvent('enter', this.events['sepehr-categories.enter']);
    }

    // Channels List

    private loadChannelsByCategoryId(categoryId: number): void {
        const self = this;
        let service: any;
        if (categoryId < 0) {
            // load all
            service = this.service.getChannels();
        } else {
            service = this.service.getChannelsByCategory(categoryId)
        }
        service.done((response: DefaultResponse<SepehrChannel[]>) => {
            self.renderChannels(response.data, () => {
                self.removeCategoriesKeyboardInputs();
                self.handleChannelsCarousel();
            });
        });
    }

    private handleChannelsCarousel(): void {
        const self = this;
        const $el = $(".channels ul");
        if (!$el.is(':visible'))
            $el.show(1);
        $el.on('afterChange', () => {
            self.reRegisterColorButtons($el);
        });
        $el.slick({
            slidesToShow: 5,
            slidesToScroll: 1,
            vertical: true,
            centerMode: true,
            lazyLoad: 'ondemand',
        });
        $el.slick('slickGoTo', 0, true);
        this.registerChannelsKeyboardInputs($el);
    }

    private reRegisterColorButtons($el = $(".channels ul")) {
        const self = this;
        self.input.removeEvent('blue,b', self.events['sepehr-live.enter']);
        self.input.removeEvent('green,g', self.events['sepehr-epg.enter']);
        setTimeout(() => {
            self.input.addEvent('blue,b', false, self.events['sepehr-live.enter'], () => {
                self.playChannelStream($el);
            });
            self.input.addEvent('green,g', false, self.events['sepehr-epg.enter'], () => {
                self.loadEpgByChannelId($el);
            });
        }, 100);
    }

    private renderChannels(data: SepehrChannel[], callback?: (() => any)): void {
        const $channels = $('.channels');
        this.templateHelper.render(channelsTemplate, data, $channels, 'html', () => {
            $channels.addClass('active');
            if (typeof callback === 'function') {
                callback();
            }
        });
    }

    private hideChannels(): void {
        const self = this;
        const $channels = $('.channels');
        $channels.removeClass('active');
        this.removeChannelKeyInputs();
        setTimeout(() => {
            $channels.empty();
            self.registerCategoriesKeyboardInputs($("ul.sepehr-categories"));
        }, 100);
    }

    private registerChannelsKeyboardInputs($carousel?): void {
        $carousel = typeof $carousel !== 'undefined' && $carousel ? $carousel : $(".channels ul");
        const self = this;
        this.input.removeEvent('back,backspace', this.events['sepehr-categories.back']);
        setTimeout(() => {
            self.input.addEvent('back,backspace', false, this.events['sepehr-channels.back'], () => {
                self.hideChannels();
            });
        }, 50);
        this.input.addEvent('up', false, this.events['sepehr-channels.prev'], () => {
            $carousel.slick('slickPrev');
        });
        this.input.addEvent('down', false, this.events['sepehr-channels.next'], () => {
            $carousel.slick('slickNext');
        });
    }

    private removeChannelKeyInputs(backFromVideo: boolean = false): void {
        const self = this;
        this.input.removeEvent('back,backspace', self.events['sepehr-channels.back']);
        this.input.removeEvent('up', this.events['sepehr-channels.prev']);
        this.input.removeEvent('down', this.events['sepehr-channels.next']);
        this.input.removeEvent('blue,b', this.events['sepehr-live.enter']);
        this.input.removeEvent('green,g', this.events['sepehr-epg.enter']);
        if (!backFromVideo) {
            setTimeout(() => {
                self.layoutInstance.prepareUnloadModule();
            }, 100);
        }
    }

    // Live Stream
    private playChannelStream($carousel): void {
        const self = this;
        if (this.templateHelper.hasClass('player-mode'))
            return;
        const $current = $carousel.find('.slick-current.slick-center li');
        const playerParams = {
            unloadMethod: () => {
                self.registerChannelsKeyboardInputs();
                self.reRegisterColorButtons();
                self.playerInstance.unload();
            },
            liveui: true,
            poster: $current.find('figure > img').attr('src'),
            sources: [{
                src: $current.data('media'),
                type: 'application/x-mpegURL',
            }],
            html5: {
                vhs: {
                    overrideNative: true
                }
            }
        };
        this.removeChannelKeyInputs(true);
        this.playerInstance = new this.playerService('mediaplayer', playerParams);
    }

    // EPG
    private loadEpgByChannelId($carousel): void {
        moment.locale('en');
        const self = this;
        const $current = $carousel.find('.slick-current.slick-center li');
        this.templateHelper.loading();
        this.service.getEpg($current.data('id'), moment().format('YYYY-MM-DD')).done((response: DefaultResponse<SepehrEpg[]>) => {
            self.renderEpg(response.data, () => {
                self.initializeEpgSlider();
                self.templateHelper.loading(false);
                $('.epg').addClass('active');
            })
        });
    }

    private renderEpg(items: SepehrEpg[], callback): void {
        this.templateHelper.render(epgTemplate, { items }, $('.epg'), 'html', () => {
            if (typeof callback === 'function')
                callback(items);
        });
    }

    private initializeEpgSlider(): void {
        const self = this;
        const $el = $("ul.schedule-items");
        const slidesToShow = 5;
        if (!$el.is(':visible'))
            $el.show(1);
        $el.on('init', () => {
            setTimeout(() => {
                self.goToCurrentEpgItem($el);
            }, 50);
        });
        $el.slick({
            slidesToShow: slidesToShow,
            slidesToScroll: 1,
            vertical: true,
            centerMode: true,
            lazyLoad: 'ondemand',
        });
        this.registerEpgKeyboardInputs($el);
    }

    private goToCurrentEpgItem($carousel): void {
        const self = this;
        try {
            const $current = $carousel.find('li.current').parents('.slick-slide:first');
            $carousel.slick('slickGoTo', $current.attr('data-slick-index'), true);
        } catch (e) {
            setTimeout(() => {
                self.goToCurrentEpgItem($carousel);
            }, 50);
            return;
        }
    }

    private registerEpgKeyboardInputs($carousel = $("ul.schedule-items")): void {
        const self = this;
        this.removeChannelKeyInputs();
        this.input.addEvent('back,backspace', false, this.events['sepehr-epg.back'], () => {
            $('.epg').empty().removeClass('active');
            self.removeEpgKeyboardListeners();
        });
        this.input.addEvent('up', false, this.events['sepehr-epg.prev'], () => {
            $carousel.slick('slickPrev');
        });
        this.input.addEvent('down', false, this.events['sepehr-epg.next'], () => {
            $carousel.slick('slickNext');
        });
    }

    private removeEpgKeyboardListeners(): void {
        const self = this;
        this.input.removeEvent('back,backspace', this.events['sepehr-epg.back']);
        this.input.removeEvent('up', this.events['sepehr-epg.prev']);
        this.input.removeEvent('down', this.events['sepehr-epg.next']);
        setTimeout(() => {
            self.registerChannelsKeyboardInputs();
        })
    }
}