-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(content): Adding download button to track content page
- Loading branch information
Showing
7 changed files
with
160 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
import {TrackContentPage} from './page/track-content-page'; | ||
|
||
new TrackContentPage().init(); | ||
new TrackContentPage().bootstrap(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,48 @@ | ||
import * as $ from 'jquery'; | ||
import {Subscription} from 'rxjs/Subscription'; | ||
import {domElementRemoved$} from './dom-utils'; | ||
|
||
export abstract class ContentPage { | ||
protected constructor(protected readonly id: string, | ||
protected readonly shouldLoad: () => boolean, | ||
protected readonly onLoad: () => void) { | ||
protected subscriptions: Subscription = new Subscription(); | ||
|
||
protected constructor(protected readonly id: string) { | ||
} | ||
|
||
public init() { | ||
public bootstrap() { | ||
if (this.shouldLoad()) { | ||
if (!contentLoaded(this.id)) { | ||
loadContent(this.id, this.onLoad); | ||
if (!this.hasInitialized()) { | ||
this.initialize(); | ||
} | ||
} else { | ||
unloadContent(this.id); | ||
if (!this.hasUninitialized()) { | ||
this.unInitialize(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
function contentLoaded(id: string): boolean { | ||
return $(`#${id}`).length > 0; | ||
} | ||
protected abstract shouldLoad(): boolean; | ||
|
||
function loadContent(id: string, onLoad: () => void) { | ||
$('body').append($('<div/>', {id})); | ||
onLoad(); | ||
} | ||
protected abstract onInit(): void; | ||
|
||
private hasInitialized(): boolean { | ||
return $(`#${this.id}`).length > 0; | ||
} | ||
|
||
private hasUninitialized(): boolean { | ||
return $(`#${this.id}`).length === 0; | ||
} | ||
|
||
function unloadContent(id: string) { | ||
$(`#${id}`).remove(); | ||
private initialize(): void { | ||
console.log('(ZC): Initializing content page', this.id); | ||
const contentPageTag = $('<div/>', {id: this.id}); | ||
$('body').append(contentPageTag); | ||
this.subscriptions.add(domElementRemoved$(contentPageTag[0]).subscribe(() => this.unInitialize())); | ||
this.onInit(); | ||
} | ||
|
||
private unInitialize(): void { | ||
console.log('(ZC): Un-initializing content page', this.id); | ||
$(`#${this.id}`).remove(); | ||
this.subscriptions.unsubscribe(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import * as $ from 'jquery'; | ||
import 'rxjs/add/observable/fromEventPattern'; | ||
import {Observable} from 'rxjs/Observable'; | ||
|
||
export function domElementRemoved$(elem: Node): Observable<boolean> { | ||
let mutationObserver: MutationObserver; | ||
return Observable.fromEventPattern<boolean>( | ||
(handler: (signal: boolean) => void) => { | ||
mutationObserver = new MutationObserver((mutations: MutationRecord[]) => { | ||
mutations.forEach((mutation: MutationRecord) => { | ||
const nodes = $.makeArray(mutation.removedNodes); | ||
if (nodes.some((node: Node) => node.contains(elem))) { | ||
handler(true); | ||
} | ||
}); | ||
}); | ||
mutationObserver.observe(document.body, { | ||
childList: true, | ||
subtree: true | ||
}); | ||
return mutationObserver; | ||
}, | ||
() => { | ||
if (mutationObserver) { | ||
mutationObserver.disconnect(); | ||
} | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,86 @@ | ||
import * as $ from 'jquery'; | ||
import {SC_BUTTON_CLASSES, ZC_DL_BUTTON_CLASS, ZC_TRACK_DL_BUTTON_ID} from '../constants'; | ||
import 'rxjs/add/observable/interval'; | ||
import 'rxjs/add/operator/delay'; | ||
import {Observable} from 'rxjs/Observable'; | ||
import {Subject} from 'rxjs/Subject'; | ||
import {ZC_DL_BUTTON_CLASS} from '../constants'; | ||
import {ContentPage} from './content-page'; | ||
import {domElementRemoved$} from './dom-utils'; | ||
|
||
export class TrackContentPage extends ContentPage { | ||
protected injectContent$: Subject<boolean>; | ||
|
||
constructor() { | ||
super('zc-track-content', shouldLoad, onLoad); | ||
super('zc-track-content'); | ||
} | ||
|
||
protected shouldLoad(): boolean { | ||
const TRACK_URL_PATTERN = /^[^:]*:\/\/soundcloud\.com\/([^\/]+)\/([^\/]+)(?:\?in=.+)?$/; | ||
const TRACK_URL_BLACKLIST_1 = ['you', 'charts', 'pages', 'settings', 'jobs', 'tags', 'stations']; | ||
const TRACK_URL_BLACKLIST_2 = ['stats']; | ||
const matchResults = TRACK_URL_PATTERN.exec(document.location.href); | ||
return matchResults && | ||
(TRACK_URL_BLACKLIST_1.indexOf(matchResults[1]) < 0) && | ||
(TRACK_URL_BLACKLIST_2.indexOf(matchResults[2]) < 0); | ||
} | ||
|
||
protected onInit(): void { | ||
this.injectContent$ = Subject.create(null, Observable.interval(10000).delay(1000)); | ||
this.subscriptions.add(this.injectContent$.subscribe(this.injectContents.bind(this))); | ||
} | ||
|
||
private injectContents(): void { | ||
if (!downloadButtonExists()) { | ||
console.log('(ZC): Injecting contents'); | ||
const soundActions = getSoundActionsToolbar(); | ||
if (soundActions.length) { | ||
const dlButton = createDownloadButton(soundActions); | ||
dlButton.on('click', () => console.log('(ZC): Clicked download button!')); | ||
addDownloadButton(soundActions, dlButton); | ||
this.subscriptions.add(domElementRemoved$(dlButton[0]).subscribe(() => { | ||
console.log('Button removed!'); | ||
this.injectContent$.next(true); | ||
})); | ||
} | ||
} | ||
} | ||
} | ||
|
||
function shouldLoad() { | ||
const TRACK_URL_PATTERN = /^[^:]*:\/\/soundcloud\.com\/([^\/]+)\/([^\/]+)(?:\?in=.+)?$/; | ||
const TRACK_URL_BLACKLIST_1 = ['you', 'charts', 'pages', 'settings', 'jobs', 'tags', 'stations']; | ||
const TRACK_URL_BLACKLIST_2 = ['stats']; | ||
const matchResults = TRACK_URL_PATTERN.exec(document.location.href); | ||
return matchResults && | ||
(TRACK_URL_BLACKLIST_1.indexOf(matchResults[1]) < 0) && | ||
(TRACK_URL_BLACKLIST_2.indexOf(matchResults[2]) < 0); | ||
function downloadButtonExists(): boolean { | ||
return $('#zcTrackDlButton').length > 0; | ||
} | ||
|
||
function onLoad() { | ||
console.log('(ZC): Loaded track content script'); | ||
const downloadButton = $('<button/>') | ||
.addClass(SC_BUTTON_CLASSES) | ||
function getSoundActionsToolbar(): JQuery<HTMLElement> { | ||
let soundActions = $('.listenEngagement .soundActions'); | ||
if (!!soundActions.length) { | ||
soundActions = $('.soundActions.soundActions__medium'); | ||
} | ||
return soundActions.first(); | ||
} | ||
|
||
function createDownloadButton(soundActions: JQuery<HTMLElement>): JQuery<HTMLElement> { | ||
const dlButton = $('<button/>') | ||
.addClass(['sc-button', 'sc-button-medium']) | ||
.addClass(ZC_DL_BUTTON_CLASS) | ||
.attr('id', ZC_TRACK_DL_BUTTON_ID) | ||
.prop('title', 'Download this track') | ||
.on('click', () => console.log('(ZC): Clicked download button!')); | ||
.attr('id', 'zcTrackDlButton') | ||
.prop('title', 'Download this track'); | ||
if ($(soundActions).find('.sc-button-responsive').length) { | ||
dlButton.addClass('sc-button-responsive'); | ||
dlButton.html('Download'); | ||
} else { | ||
dlButton.addClass('sc-button-icon'); | ||
} | ||
return dlButton; | ||
} | ||
|
||
function addDownloadButton(soundActions: JQuery<HTMLElement>, dlButton: JQuery<HTMLElement>): void { | ||
const buttonGroup = soundActions.children('div').first(); | ||
if (buttonGroup.length) { | ||
const lastButtonInGroup = buttonGroup.children('button').last(); | ||
if (lastButtonInGroup.hasClass('sc-button-more')) { | ||
dlButton.insertBefore(lastButtonInGroup); | ||
} else { | ||
buttonGroup.append(dlButton); | ||
} | ||
} | ||
} |