├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── app.png ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.module.ts │ ├── components │ │ ├── cue │ │ │ ├── cue.component.html │ │ │ ├── cue.component.scss │ │ │ └── cue.component.ts │ │ ├── discover │ │ │ ├── discover.component.html │ │ │ ├── discover.component.scss │ │ │ └── discover.component.ts │ │ ├── feed │ │ │ ├── feed.component.html │ │ │ ├── feed.component.scss │ │ │ └── feed.component.ts │ │ ├── home │ │ │ ├── home.component.html │ │ │ ├── home.component.scss │ │ │ └── home.component.ts │ │ ├── likes │ │ │ ├── likes.component.html │ │ │ ├── likes.component.scss │ │ │ └── likes.component.ts │ │ ├── main │ │ │ ├── main.component.html │ │ │ └── main.component.ts │ │ ├── menu │ │ │ ├── menu.component.html │ │ │ └── menu.component.ts │ │ ├── search │ │ │ ├── search.component.html │ │ │ └── search.component.ts │ │ └── soon │ │ │ ├── soon.component.html │ │ │ ├── soon.component.scss │ │ │ └── soon.component.ts │ ├── helpers.ts │ ├── services │ │ ├── Dz.service.ts │ │ ├── Ebus.service.ts │ │ ├── Likes.service.ts │ │ └── cache.ts │ └── static │ │ └── GenresMap.js ├── assets │ ├── .gitkeep │ └── img │ │ ├── 404.png │ │ └── logo.png ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── sass │ ├── app.scss │ └── blocks │ │ ├── _animate.scss │ │ ├── _base.scss │ │ ├── _cue.scss │ │ ├── _feed.scss │ │ ├── _layout.scss │ │ ├── _likes.scss │ │ ├── _loader.scss │ │ ├── _main.scss │ │ ├── _menu.scss │ │ └── _search.scss ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TuneIn 🎶 2 | This is a javascript project developed with Angular 6. 3 | 4 | Simple and elegant music discovery app 5 | 6 | ⭐️ this repo if you like it. 7 | 8 | ## Getting Started 🚀 9 | 10 | - Clone the repo 11 | - Install the Angular cli if you dont have it installed with ``` npm install -g @angular/cli ``` 12 | - Install the dependicies with ``` npm install``` 13 | - Start the development server with ``` ng serve ``` 14 | The App should be running on localhost port 4200 15 | 16 | ## Preview 📸 17 | ### https://ng-tunein.netlify.com/ 18 | 19 | 20 | 21 | ## Contact me 📧 22 | #### Email : mrbouaggadmoez@gmail.com 23 | #### Website : https://bouaggadmoez.netlify.com/ 24 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "soundcloud": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "styleext": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/soundcloud", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "node_modules/font-awesome/css/font-awesome.min.css", 31 | "src/sass/app.scss" 32 | ], 33 | "scripts": [] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "extractCss": true, 47 | "namedChunks": false, 48 | "aot": true, 49 | "extractLicenses": true, 50 | "vendorChunk": false, 51 | "buildOptimizer": true 52 | } 53 | } 54 | }, 55 | "serve": { 56 | "builder": "@angular-devkit/build-angular:dev-server", 57 | "options": { 58 | "browserTarget": "soundcloud:build" 59 | }, 60 | "configurations": { 61 | "production": { 62 | "browserTarget": "soundcloud:build:production" 63 | } 64 | } 65 | }, 66 | "extract-i18n": { 67 | "builder": "@angular-devkit/build-angular:extract-i18n", 68 | "options": { 69 | "browserTarget": "soundcloud:build" 70 | } 71 | }, 72 | "test": { 73 | "builder": "@angular-devkit/build-angular:karma", 74 | "options": { 75 | "main": "src/test.ts", 76 | "polyfills": "src/polyfills.ts", 77 | "tsConfig": "src/tsconfig.spec.json", 78 | "karmaConfig": "src/karma.conf.js", 79 | "styles": [ 80 | "src/styles.scss" 81 | ], 82 | "scripts": [], 83 | "assets": [ 84 | "src/favicon.ico", 85 | "src/assets" 86 | ] 87 | } 88 | }, 89 | "lint": { 90 | "builder": "@angular-devkit/build-angular:tslint", 91 | "options": { 92 | "tsConfig": [ 93 | "src/tsconfig.app.json", 94 | "src/tsconfig.spec.json" 95 | ], 96 | "exclude": [ 97 | "**/node_modules/**" 98 | ] 99 | } 100 | } 101 | } 102 | }, 103 | "soundcloud-e2e": { 104 | "root": "e2e/", 105 | "projectType": "application", 106 | "architect": { 107 | "e2e": { 108 | "builder": "@angular-devkit/build-angular:protractor", 109 | "options": { 110 | "protractorConfig": "e2e/protractor.conf.js", 111 | "devServerTarget": "soundcloud:serve" 112 | }, 113 | "configurations": { 114 | "production": { 115 | "devServerTarget": "soundcloud:serve:production" 116 | } 117 | } 118 | }, 119 | "lint": { 120 | "builder": "@angular-devkit/build-angular:tslint", 121 | "options": { 122 | "tsConfig": "e2e/tsconfig.e2e.json", 123 | "exclude": [ 124 | "**/node_modules/**" 125 | ] 126 | } 127 | } 128 | } 129 | } 130 | }, 131 | "defaultProject": "soundcloud" 132 | } -------------------------------------------------------------------------------- /app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/app.png -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to soundcloud!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soundcloud", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.0.3", 15 | "@angular/common": "^6.0.3", 16 | "@angular/compiler": "^6.0.3", 17 | "@angular/core": "^6.0.3", 18 | "@angular/forms": "^6.0.3", 19 | "@angular/http": "^6.0.3", 20 | "@angular/platform-browser": "^6.0.3", 21 | "@angular/platform-browser-dynamic": "^6.0.3", 22 | "@angular/router": "^6.0.3", 23 | "core-js": "^2.5.4", 24 | "font-awesome": "4.7", 25 | "materialize-css": "^0.100.2", 26 | "rxjs": "^6.0.0", 27 | "zone.js": "^0.8.29" 28 | }, 29 | "devDependencies": { 30 | "@angular-devkit/build-angular": "~0.6.8", 31 | "@angular/cli": "~6.0.8", 32 | "@angular/compiler-cli": "^6.0.3", 33 | "@angular/language-service": "^6.0.3", 34 | "@types/jasmine": "~3.3.13", 35 | "@types/jasminewd2": "~2.0.3", 36 | "@types/node": "~8.9.4", 37 | "codelyzer": "~4.2.1", 38 | "jasmine-core": "~3.4.0", 39 | "jasmine-spec-reporter": "~4.2.1", 40 | "karma": "~1.7.1", 41 | "karma-chrome-launcher": "~2.2.0", 42 | "karma-coverage-istanbul-reporter": "~2.0.0", 43 | "karma-jasmine": "~1.1.1", 44 | "karma-jasmine-html-reporter": "^0.2.2", 45 | "protractor": "~5.3.0", 46 | "ts-node": "~5.0.1", 47 | "tslint": "~5.9.1", 48 | "typescript": "~2.7.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { DiscoverComponent } from './components/discover/discover.component'; 4 | import { SoonComponent } from './components/soon/soon.component'; 5 | import { SearchComponent } from './components/search/search.component'; 6 | import { LikesComponent } from './components/likes/likes.component'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path:'', 11 | component:DiscoverComponent, 12 | }, 13 | { 14 | path:'search', 15 | component:SearchComponent, 16 | }, 17 | { 18 | path:'likes', 19 | component:LikesComponent, 20 | }, 21 | { 22 | path:'404', 23 | component:SoonComponent, 24 | }, 25 | ]; 26 | 27 | @NgModule({ 28 | imports: [RouterModule.forRoot(routes)], 29 | exports: [RouterModule] 30 | }) 31 | export class AppRoutingModule { } 32 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
-------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http'; 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { FormsModule } from '@angular/forms'; 6 | 7 | import { AppComponent } from './app.component'; 8 | import { HomeComponent } from './components/home/home.component'; 9 | import { MenuComponent } from './components/menu/menu.component'; 10 | import { MainComponent } from './components/main/main.component'; 11 | import { CueComponent } from './components/cue/cue.component'; 12 | import { FeedComponent } from './components/feed/feed.component'; 13 | import { DzService } from './services/Dz.service'; 14 | import { DiscoverComponent } from './components/discover/discover.component'; 15 | import { SoonComponent } from './components/soon/soon.component'; 16 | import { SearchComponent } from './components/search/search.component'; 17 | import { LikesComponent } from './components/likes/likes.component'; 18 | import { EbusService } from './services/Ebus.service'; 19 | import { LikesService } from './services/Likes.service'; 20 | 21 | 22 | @NgModule({ 23 | declarations: [ 24 | AppComponent, 25 | HomeComponent, 26 | MenuComponent, 27 | MainComponent, 28 | CueComponent, 29 | FeedComponent, 30 | DiscoverComponent, 31 | SoonComponent, 32 | SearchComponent, 33 | LikesComponent 34 | ], 35 | imports: [ 36 | BrowserModule, 37 | AppRoutingModule, 38 | HttpClientModule, 39 | HttpClientJsonpModule, 40 | FormsModule 41 | 42 | ], 43 | providers: [DzService,EbusService,LikesService], 44 | bootstrap: [AppComponent] 45 | }) 46 | export class AppModule { } 47 | -------------------------------------------------------------------------------- /src/app/components/cue/cue.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Tune

3 |
4 |
5 |
6 |

Now playing

7 | 8 | {{shorten(song.title,30)}} 9 | {{song.artist.name}} 10 |
11 | 12 | 13 | 14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/app/components/cue/cue.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/cue/cue.component.scss -------------------------------------------------------------------------------- /src/app/components/cue/cue.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit,HostBinding } from '@angular/core'; 2 | import {isLiked,abbreviate,shorten} from "../../helpers"; 3 | import { EbusService } from '../../services/Ebus.service'; 4 | 5 | 6 | @Component({ 7 | selector: '[appCue]', 8 | templateUrl: './cue.component.html', 9 | styleUrls: ['./cue.component.scss'] 10 | }) 11 | export class CueComponent implements OnInit{ 12 | constructor(private bus:EbusService){} 13 | @HostBinding('class') cueClass:string='cue'; 14 | 15 | song:any=null; 16 | loading:boolean=true; 17 | playing:boolean=false; 18 | audio:any=null; 19 | prev:any=null; 20 | next:any=null; 21 | 22 | ngOnInit(){ 23 | this.bus.ebus.subscribe(payload=>{ 24 | let {song,autoplay}=payload; 25 | if(song===this.song) return; 26 | this.loading=true; 27 | this.prev =this.song; 28 | if(this.audio) this.pause(); 29 | this.song=song; 30 | this.audio=new Audio(this.song.preview); 31 | if(autoplay) this.play(); 32 | if(this.prev){ 33 | if(this.prev.album.cover_medium==this.song.album.cover_medium){ 34 | setTimeout(() => { 35 | this.loading=false; 36 | }, 50); 37 | } 38 | } 39 | this.audio.addEventListener('ended',()=>{ 40 | this.playing=false; 41 | }) 42 | }) 43 | } 44 | 45 | shorten(a,b){ 46 | return shorten(a,b) 47 | } 48 | play(){ 49 | this.playing=true; 50 | this.audio.play(); 51 | } 52 | pause(){ 53 | this.audio.pause(); 54 | this.playing=false; 55 | } 56 | goPrev(){ 57 | // if(this.prev) 58 | // Ebus.$emit('newCue',this.prev,true); 59 | } 60 | loadingEnd(){ 61 | this.loading=false; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/app/components/discover/discover.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Discover

4 |
5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/components/discover/discover.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/discover/discover.component.scss -------------------------------------------------------------------------------- /src/app/components/discover/discover.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostBinding } from '@angular/core'; 2 | import Genres from "../../static/GenresMap"; 3 | 4 | @Component({ 5 | selector: 'app-discover', 6 | templateUrl: './discover.component.html', 7 | styleUrls: ['./discover.component.scss'] 8 | }) 9 | export class DiscoverComponent implements OnInit { 10 | 11 | @HostBinding('class') discover:string='Discover'; 12 | constructor(){ 13 | this.getGenre(2098157264,132); 14 | } 15 | 16 | genres=Genres; 17 | genreId:132; 18 | playlistId:2098157264; 19 | 20 | 21 | ngOnInit() { 22 | } 23 | 24 | getGenre(playlistId,genreId){ 25 | this.playlistId=playlistId 26 | this.genreId=genreId; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/components/feed/feed.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 17 |
-------------------------------------------------------------------------------- /src/app/components/feed/feed.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/feed/feed.component.scss -------------------------------------------------------------------------------- /src/app/components/feed/feed.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges } from '@angular/core'; 2 | import {isLiked,abbreviate,shorten} from "../../helpers"; 3 | import { DzService } from '../../services/Dz.service'; 4 | import {EbusService} from '../../services/Ebus.service'; 5 | import {LikesService} from '../../services/Likes.service'; 6 | 7 | 8 | @Component({ 9 | selector: 'app-feed', 10 | templateUrl: './feed.component.html', 11 | styleUrls: ['./feed.component.scss'] 12 | }) 13 | export class FeedComponent implements OnChanges{ 14 | 15 | @Input() id:string; 16 | feed:any=[]; 17 | loading:boolean=true; 18 | 19 | constructor(public dz:DzService,public ebus:EbusService,public love:LikesService) {} 20 | 21 | ngOnChanges(change){ 22 | if(!change.id) return; 23 | this.feed=[]; 24 | this.loading=true; 25 | this.dz.playlist(this.id).subscribe(data=>{ 26 | this.feed=data; 27 | this.loading=false; 28 | }) 29 | } 30 | 31 | getBgImg(src){ 32 | return { backgroundImage: `url(${src})` } 33 | } 34 | 35 | shorten(a,b){ 36 | return shorten(a,b); 37 | } 38 | 39 | likeSong(song){ 40 | if(isLiked(this.love.likes,song.id)){ 41 | this.love.unlikeSong(song); 42 | }else{ 43 | this.love.likeSong(song); 44 | } 45 | } 46 | isLiked(a,b){ 47 | return isLiked(a,b) 48 | } 49 | 50 | cue(song){ 51 | this.ebus.ebus.emit({song,autoplay:false}); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/home/home.component.scss -------------------------------------------------------------------------------- /src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit} from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: '[appHome]', 6 | templateUrl:'./home.component.html', 7 | styleUrls: ['./home.component.scss'] 8 | }) 9 | export class HomeComponent { 10 | 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/app/components/likes/likes.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Favorite Tunes

3 |
4 | 5 |
6 | 16 |
17 | -------------------------------------------------------------------------------- /src/app/components/likes/likes.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/likes/likes.component.scss -------------------------------------------------------------------------------- /src/app/components/likes/likes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostBinding } from '@angular/core'; 2 | import {isLiked,abbreviate,shorten} from "../../helpers"; 3 | import {LikesService} from '../../services/Likes.service'; 4 | import { EbusService } from '../../services/Ebus.service'; 5 | 6 | 7 | @Component({ 8 | selector: 'app-likes', 9 | templateUrl: './likes.component.html', 10 | styleUrls: ['./likes.component.scss'] 11 | }) 12 | export class LikesComponent{ 13 | 14 | @HostBinding('class') LikesClass:string ='Likes animated fadeIn'; 15 | 16 | 17 | constructor(public love:LikesService,public ebus:EbusService) { 18 | } 19 | 20 | 21 | 22 | shorten(a,b){ 23 | return shorten(a,b); 24 | } 25 | 26 | removeFav(song){ 27 | this.love.unlikeSong(song); 28 | } 29 | 30 | cue(song){ 31 | this.ebus.ebus.emit({song,autoplay:false}); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/components/main/main.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/components/main/main.component.ts: -------------------------------------------------------------------------------- 1 | import { Component,HostBinding } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: '[appMain]', 6 | templateUrl: './main.component.html', 7 | }) 8 | export class MainComponent{ 9 | 10 | @HostBinding('class') mainClass:string ='main'; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/app/components/menu/menu.component.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 24 | 25 | 43 | 44 |
45 | 46 | Github 47 |
48 | -------------------------------------------------------------------------------- /src/app/components/menu/menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit,HostBinding } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: '[appMenu]', 5 | templateUrl: './menu.component.html', 6 | }) 7 | export class MenuComponent implements OnInit { 8 | @HostBinding('class') menuClass:string='menu'; 9 | 10 | 11 | constructor() { } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/components/search/search.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 |

Search results for "{{term}}"

8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 |
17 | {{shorten(res.title,12)}} 18 | {{shorten(res.artist.name,12)}} 19 |
20 | 21 |
22 |
23 |
-------------------------------------------------------------------------------- /src/app/components/search/search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostBinding } from '@angular/core'; 2 | import { DzService } from '../../services/Dz.service'; 3 | import {shorten} from "../../helpers"; 4 | import { EbusService } from '../../services/Ebus.service'; 5 | 6 | 7 | @Component({ 8 | selector: 'app-search', 9 | templateUrl: './search.component.html' 10 | }) 11 | export class SearchComponent{ 12 | 13 | @HostBinding('class') sClass:string ='search animated fadeIn'; 14 | 15 | 16 | constructor(private dz:DzService,private ebus:EbusService) { } 17 | 18 | term:string=''; 19 | searchResults:any=null; 20 | loading:boolean=false; 21 | 22 | search(e:Event){ 23 | this.loading=true; 24 | e.preventDefault(); 25 | this.term= e.target[0].value; 26 | this.dz.search(this.term).subscribe(data=>{ 27 | this.searchResults=data; 28 | this.loading=false; 29 | }) 30 | } 31 | cue(song){ 32 | this.ebus.ebus.emit({song,autoplay:false}); 33 | } 34 | getBgImg(src){ 35 | return { backgroundImage: `url(${src})` } 36 | } 37 | shorten(a,b){ 38 | return shorten(a,b); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/app/components/soon/soon.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Not available yet

3 | -------------------------------------------------------------------------------- /src/app/components/soon/soon.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/app/components/soon/soon.component.scss -------------------------------------------------------------------------------- /src/app/components/soon/soon.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostBinding } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-soon', 5 | templateUrl: './soon.component.html', 6 | styleUrls: ['./soon.component.scss'] 7 | }) 8 | export class SoonComponent implements OnInit { 9 | @HostBinding('class') workClass:string='work'; 10 | 11 | constructor() { } 12 | 13 | ngOnInit() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/helpers.ts: -------------------------------------------------------------------------------- 1 | 2 | export const isLiked=(likes,id)=>{ 3 | let song = likes.filter(song=>song.id===id); 4 | if(song.length===1) return true; 5 | return false; 6 | } 7 | 8 | export const unlike=(likes,id)=>{ 9 | return likes.filter(song=>song.id!==id); 10 | } 11 | 12 | export const shorten = (text,n)=> { 13 | return (text.length > n) ? text.substr(0, n - 1) + '...' : text; 14 | }; 15 | 16 | 17 | export const abbreviate=(number, maxPlaces, forcePlaces, forceLetter)=>{ 18 | number = Number(number) 19 | forceLetter = forceLetter || false 20 | if (forceLetter !== false) { 21 | return annotate(number, maxPlaces, forcePlaces, forceLetter) 22 | } 23 | var abbr 24 | if (number >= 1e12) { 25 | abbr = 'T' 26 | } 27 | else if (number >= 1e9) { 28 | abbr = 'B' 29 | } 30 | else if (number >= 1e6) { 31 | abbr = 'M' 32 | } 33 | else if (number >= 1e3) { 34 | abbr = 'K' 35 | } 36 | else { 37 | abbr = '' 38 | } 39 | return annotate(number, maxPlaces, forcePlaces, abbr) 40 | } 41 | 42 | function annotate(number, maxPlaces, forcePlaces, abbr) { 43 | // set places to false to not round 44 | var rounded:any = 0 45 | switch (abbr) { 46 | case 'T': 47 | rounded = number / 1e12 48 | break 49 | case 'B': 50 | rounded = number / 1e9 51 | break 52 | case 'M': 53 | rounded = number / 1e6 54 | break 55 | case 'K': 56 | rounded = number / 1e3 57 | break 58 | case '': 59 | rounded = number 60 | break 61 | } 62 | if (maxPlaces !== false) { 63 | var test = new RegExp('\\.\\d{' + (maxPlaces + 1) + ',}$') 64 | if (test.test(('' + rounded))) { 65 | rounded = rounded.toFixed(maxPlaces) 66 | } 67 | } 68 | if (forcePlaces !== false) { 69 | rounded = Number(rounded).toFixed(forcePlaces) 70 | } 71 | return rounded + abbr 72 | } -------------------------------------------------------------------------------- /src/app/services/Dz.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { HttpClient } from "@angular/common/http"; 3 | import { map } from "rxjs/operators"; 4 | import store from "./cache"; 5 | 6 | 7 | @Injectable() 8 | export class DzService{ 9 | constructor(private http:HttpClient){} 10 | call(url){ 11 | if(store.has(url)) return store.get(url); 12 | return this.http.jsonp(url,'callback') 13 | .pipe(map((response:any)=>{ 14 | return store.set(response.data,url) 15 | })) 16 | } 17 | playlist(id){ 18 | let url = `https://api.deezer.com/playlist/${id}/tracks?output=jsonp`; 19 | return this.call(url); 20 | } 21 | search(term){ 22 | let url = `https://api.deezer.com/search?q=${term}&output=jsonp&limit=24`; 23 | return this.call(url); 24 | } 25 | } -------------------------------------------------------------------------------- /src/app/services/Ebus.service.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "@angular/core"; 2 | 3 | export class EbusService { 4 | public ebus :EventEmitter; 5 | constructor(){ 6 | this.ebus= new EventEmitter(); 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/app/services/Likes.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | 3 | @Injectable({ 4 | providedIn:'root' 5 | }) 6 | export class LikesService { 7 | likes:any=JSON.parse(localStorage.getItem('likes'))||[]; 8 | 9 | likeSong(song){ 10 | this.likes.push(song); 11 | localStorage.setItem('likes',JSON.stringify(this.likes)); 12 | } 13 | unlikeSong(song){ 14 | this.likes=this.likes.filter(s=>s.id!=song.id); 15 | localStorage.setItem('likes', JSON.stringify(this.likes)); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/app/services/cache.ts: -------------------------------------------------------------------------------- 1 | import { of } from "rxjs/" 2 | 3 | class Store { 4 | store={}; 5 | 6 | has(url){ 7 | return!!this.store[url] 8 | } 9 | set(data,url){ 10 | this.store[url]=JSON.stringify(data); 11 | return data; 12 | } 13 | 14 | get(url){ 15 | return of(JSON.parse(this.store[url])); 16 | } 17 | } 18 | 19 | export default new Store(); -------------------------------------------------------------------------------- /src/app/static/GenresMap.js: -------------------------------------------------------------------------------- 1 | export default [{ "id": "132", "name": "Pop", "picture": "https://cdns-images.dzcdn.net/images/misc/db7a604d9e7634a67d45cfc86b48370a/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/db7a604d9e7634a67d45cfc86b48370a/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/db7a604d9e7634a67d45cfc86b48370a/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/db7a604d9e7634a67d45cfc86b48370a/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/db7a604d9e7634a67d45cfc86b48370a/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2098157264, "title": "Hits of the Moment" }, { "id": "116", "name": "Rap/Hip Hop", "picture": "https://cdns-images.dzcdn.net/images/misc/5c27115d3b797954afff59199dad98d1/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/5c27115d3b797954afff59199dad98d1/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/5c27115d3b797954afff59199dad98d1/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/5c27115d3b797954afff59199dad98d1/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/5c27115d3b797954afff59199dad98d1/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1450184495, "title": "Hip Hop 2018 : Top 100 Rap HITS 2018" }, { "id": "152", "name": "Rock", "picture": "https://cdns-images.dzcdn.net/images/misc/b36ca681666d617edd0dcb5ab389a6ac/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/b36ca681666d617edd0dcb5ab389a6ac/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/b36ca681666d617edd0dcb5ab389a6ac/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/b36ca681666d617edd0dcb5ab389a6ac/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/b36ca681666d617edd0dcb5ab389a6ac/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1306931615, "title": "Best Rock of All Time" }, { "id": "113", "name": "Dance", "picture": "https://cdns-images.dzcdn.net/images/misc/bd5fdfa1a23e02e2697818e09e008e69/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/bd5fdfa1a23e02e2697818e09e008e69/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/bd5fdfa1a23e02e2697818e09e008e69/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/bd5fdfa1a23e02e2697818e09e008e69/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/bd5fdfa1a23e02e2697818e09e008e69/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2249258602, "title": "New Dance" }, { "id": "165", "name": "R&B", "picture": "https://cdns-images.dzcdn.net/images/misc/68a43aec844708e693cb99f47814153b/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/68a43aec844708e693cb99f47814153b/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/68a43aec844708e693cb99f47814153b/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/68a43aec844708e693cb99f47814153b/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/68a43aec844708e693cb99f47814153b/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1282495565, "title": "Radar Weekly" }, { "id": "85", "name": "Alternative", "picture": "https://cdns-images.dzcdn.net/images/misc/fd252ab727d9a3b0b3c29014873f8f57/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/fd252ab727d9a3b0b3c29014873f8f57/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/fd252ab727d9a3b0b3c29014873f8f57/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/fd252ab727d9a3b0b3c29014873f8f57/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/fd252ab727d9a3b0b3c29014873f8f57/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1910358422, "title": "Melancholy Mood" }, { "id": "106", "name": "Electro", "picture": "https://cdns-images.dzcdn.net/images/misc/15df4502c1c58137dae5bdd1cc6f0251/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/15df4502c1c58137dae5bdd1cc6f0251/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/15df4502c1c58137dae5bdd1cc6f0251/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/15df4502c1c58137dae5bdd1cc6f0251/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/15df4502c1c58137dae5bdd1cc6f0251/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1807219322, "title": "Electro-Jazz Vibes" }, { "id": "466", "name": "Folk", "picture": "https://cdns-images.dzcdn.net/images/misc/f9e070848998df8870ba65cd0d22b2b3/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/f9e070848998df8870ba65cd0d22b2b3/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/f9e070848998df8870ba65cd0d22b2b3/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/f9e070848998df8870ba65cd0d22b2b3/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/f9e070848998df8870ba65cd0d22b2b3/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1390327745, "title": "Songwriters" }, { "id": "144", "name": "Reggae", "picture": "https://cdns-images.dzcdn.net/images/misc/7b901a98628cf879e1465f1dfd697e00/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/7b901a98628cf879e1465f1dfd697e00/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/7b901a98628cf879e1465f1dfd697e00/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/7b901a98628cf879e1465f1dfd697e00/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/7b901a98628cf879e1465f1dfd697e00/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1273315391, "title": "Reggaetón Hits" }, { "id": "129", "name": "Jazz", "picture": "https://cdns-images.dzcdn.net/images/misc/91468ecc5dfdd19c42a43d2cbdf27059/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/91468ecc5dfdd19c42a43d2cbdf27059/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/91468ecc5dfdd19c42a43d2cbdf27059/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/91468ecc5dfdd19c42a43d2cbdf27059/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/91468ecc5dfdd19c42a43d2cbdf27059/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1257789321, "title": "60 Years of Soul Music" }, { "id": "52", "name": "French Chanson", "picture": "https://cdns-images.dzcdn.net/images/misc/e0de60a12cf6cef0ced36c9b37672434/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/e0de60a12cf6cef0ced36c9b37672434/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/e0de60a12cf6cef0ced36c9b37672434/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/e0de60a12cf6cef0ced36c9b37672434/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/e0de60a12cf6cef0ced36c9b37672434/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1288071965, "title": "Chanson française - Meilleur France - Hier/Aujourd’hui - French music - Classics - Französische Lieder Chansons -" }, { "id": "98", "name": "Classical", "picture": "https://cdns-images.dzcdn.net/images/misc/609f69b669b242252aa8ee09b5597655/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/609f69b669b242252aa8ee09b5597655/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/609f69b669b242252aa8ee09b5597655/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/609f69b669b242252aa8ee09b5597655/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/609f69b669b242252aa8ee09b5597655/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1787912442, "title": "Calm Piano" }, { "id": "173", "name": "Films/Games", "picture": "https://cdns-images.dzcdn.net/images/misc/236d8057751d9c557728400dfe71483a/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/236d8057751d9c557728400dfe71483a/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/236d8057751d9c557728400dfe71483a/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/236d8057751d9c557728400dfe71483a/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/236d8057751d9c557728400dfe71483a/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2158831582, "title": "B.S.O. Films & Video games" }, { "id": "464", "name": "Metal", "picture": "https://cdns-images.dzcdn.net/images/misc/f14f9fde9feb38ca6d61960f00681860/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/f14f9fde9feb38ca6d61960f00681860/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/f14f9fde9feb38ca6d61960f00681860/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/f14f9fde9feb38ca6d61960f00681860/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/f14f9fde9feb38ca6d61960f00681860/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1050179021, "title": "Metal New Releases" }, { "id": "169", "name": "Soul & Funk", "picture": "https://cdns-images.dzcdn.net/images/misc/3d5e8aab99b95bfa7ac7e9e466e7781e/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/3d5e8aab99b95bfa7ac7e9e466e7781e/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/3d5e8aab99b95bfa7ac7e9e466e7781e/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/3d5e8aab99b95bfa7ac7e9e466e7781e/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/3d5e8aab99b95bfa7ac7e9e466e7781e/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1257789321, "title": "60 Years of Soul Music" }, { "id": "2", "name": "African Music", "picture": "https://cdns-images.dzcdn.net/images/misc/703413adf47ad8a6001b438f7608a2be/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/703413adf47ad8a6001b438f7608a2be/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/703413adf47ad8a6001b438f7608a2be/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/703413adf47ad8a6001b438f7608a2be/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/703413adf47ad8a6001b438f7608a2be/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2708423744, "title": "african music rules !!" }, { "id": "12", "name": "Arabic Music", "picture": "https://cdns-images.dzcdn.net/images/misc/2df05f13d3244dbdd1ae0206b4605cba/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/2df05f13d3244dbdd1ae0206b4605cba/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/2df05f13d3244dbdd1ae0206b4605cba/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/2df05f13d3244dbdd1ae0206b4605cba/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/2df05f13d3244dbdd1ae0206b4605cba/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 4128412802, "title": "Arabic Music 2018 اغاني عربية" }, { "id": "16", "name": "Asian Music", "picture": "https://cdns-images.dzcdn.net/images/misc/dd6d2756465b22488dff5d8663e86688/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/dd6d2756465b22488dff5d8663e86688/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/dd6d2756465b22488dff5d8663e86688/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/dd6d2756465b22488dff5d8663e86688/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/dd6d2756465b22488dff5d8663e86688/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 3030483942, "title": "Asian Zen Spa Music Meditation" }, { "id": "153", "name": "Blues", "picture": "https://cdns-images.dzcdn.net/images/misc/1abb6810098d4015bdc860c91bcfd2b6/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/1abb6810098d4015bdc860c91bcfd2b6/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/1abb6810098d4015bdc860c91bcfd2b6/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/1abb6810098d4015bdc860c91bcfd2b6/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/1abb6810098d4015bdc860c91bcfd2b6/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1689177971, "title": "Foreplay Jazz - Sexy Jazz for Love" }, { "id": "75", "name": "Brazilian Music", "picture": "https://cdns-images.dzcdn.net/images/misc/01b12a3f3582899a13b664cea703a335/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/01b12a3f3582899a13b664cea703a335/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/01b12a3f3582899a13b664cea703a335/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/01b12a3f3582899a13b664cea703a335/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/01b12a3f3582899a13b664cea703a335/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2988016026, "title": "Deep House, Brazilian Bass and House Music" }, { "id": "81", "name": "Indian Music", "picture": "https://cdns-images.dzcdn.net/images/misc/b098161d9a824eef314bc38b985594a1/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/b098161d9a824eef314bc38b985594a1/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/b098161d9a824eef314bc38b985594a1/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/b098161d9a824eef314bc38b985594a1/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/b098161d9a824eef314bc38b985594a1/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 3969048206, "title": "musica árabe/indiana" }, { "id": "95", "name": "Kids", "picture": "https://cdns-images.dzcdn.net/images/misc/b0b8efcbc3cb688864ce69da0061e525/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/b0b8efcbc3cb688864ce69da0061e525/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/b0b8efcbc3cb688864ce69da0061e525/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/b0b8efcbc3cb688864ce69da0061e525/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/b0b8efcbc3cb688864ce69da0061e525/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 2134531422, "title": "Les hits des Kids" }, { "id": "197", "name": "Latin Music", "picture": "https://cdns-images.dzcdn.net/images/misc/069c9888538799748960781f098b5f4b/180x180-000000-80-0-0.jpg", "picture_small": "https://cdns-images.dzcdn.net/images/misc/069c9888538799748960781f098b5f4b/56x56-000000-80-0-0.jpg", "picture_medium": "https://cdns-images.dzcdn.net/images/misc/069c9888538799748960781f098b5f4b/250x250-000000-80-0-0.jpg", "picture_big": "https://cdns-images.dzcdn.net/images/misc/069c9888538799748960781f098b5f4b/500x500-000000-80-0-0.jpg", "picture_xl": "https://cdns-images.dzcdn.net/images/misc/069c9888538799748960781f098b5f4b/1000x1000-000000-80-0-0.jpg", "type": "editorial", "playlistId": 1128625503, "title": "Best of Latin Music 2017" }] -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/img/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/assets/img/404.png -------------------------------------------------------------------------------- /src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/assets/img/logo.png -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TuneIn 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import './blocks/base'; 2 | @import './blocks/animate'; 3 | @import './blocks/layout'; 4 | @import './blocks/menu'; 5 | @import './blocks/main'; 6 | @import './blocks/feed'; 7 | @import './blocks/cue'; 8 | @import './blocks/loader'; 9 | @import './blocks/search'; 10 | @import './blocks/likes'; 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/sass/blocks/_animate.scss: -------------------------------------------------------------------------------- 1 | .animated { 2 | -webkit-animation-duration: 1s; 3 | animation-duration: 1s; 4 | -webkit-animation-fill-mode: both; 5 | animation-fill-mode: both; 6 | } 7 | 8 | @-webkit-keyframes fadeIn { 9 | from { 10 | opacity: 0; 11 | } 12 | 13 | to { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | @keyframes fadeIn { 19 | from { 20 | opacity: 0; 21 | } 22 | 23 | to { 24 | opacity: 1; 25 | } 26 | } 27 | 28 | .fadeIn { 29 | -webkit-animation-name: fadeIn; 30 | animation-name: fadeIn; 31 | } -------------------------------------------------------------------------------- /src/sass/blocks/_base.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | *, 7 | *::before, 8 | *::after { 9 | box-sizing: inherit; 10 | } 11 | 12 | html { 13 | box-sizing: border-box; 14 | font-size: 62.5%; 15 | } 16 | 17 | body { 18 | font-family: 'Lato', sans-serif; 19 | font-weight: 400; 20 | line-height: 1.6; 21 | color: white; 22 | } 23 | 24 | a{ 25 | text-decoration: none; 26 | } 27 | 28 | .active-link{ 29 | padding-left: .9rem; 30 | color: #fff; 31 | border-left: 4px solid #db1d40; 32 | } 33 | -------------------------------------------------------------------------------- /src/sass/blocks/_cue.scss: -------------------------------------------------------------------------------- 1 | .cue-song{ 2 | animation-duration: .3s; 3 | margin-top: 3rem; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | font-size: 1.7rem; 8 | img{ 9 | width: 100%; 10 | padding: .5rem; 11 | border-radius: 50%; 12 | background: #d8d6d6; 13 | } 14 | 15 | .spin{ 16 | -webkit-animation:spin 4s linear infinite; 17 | -moz-animation:spin 4s linear infinite; 18 | animation:spin 15s linear infinite; 19 | } 20 | 21 | @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } 22 | @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } 23 | @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } 24 | 25 | 26 | 27 | .title{ 28 | margin-top: 1.5rem; 29 | // align-self: flex-start; 30 | color: white; 31 | display: block; 32 | } 33 | .artist{ 34 | // align-self: flex-start; 35 | color: #d8d6d6; 36 | } 37 | } 38 | .playing-label{ 39 | // align-self: flex-start; 40 | font-size: 1.5rem; 41 | margin-bottom: 1.5rem; 42 | color: #e4e4e4; 43 | } 44 | 45 | .player{ 46 | display: flex; 47 | justify-content: center; 48 | align-items: center; 49 | margin: 2.5rem auto; 50 | .fa{ 51 | cursor: pointer; 52 | font-size: 2rem !important ; 53 | &-play-circle,&-pause{ 54 | font-size: 3rem !important; 55 | flex: 1; 56 | } 57 | margin: 0 4rem; 58 | } 59 | } -------------------------------------------------------------------------------- /src/sass/blocks/_feed.scss: -------------------------------------------------------------------------------- 1 | .feed{ 2 | margin-top: 5rem; 3 | } 4 | 5 | .feed__list{ 6 | list-style: none; 7 | .song{ 8 | display: flex; 9 | align-items: center; 10 | margin: 2.5rem 0; 11 | &__image{ 12 | backface-visibility: hidden; 13 | cursor: pointer; 14 | border-radius: 50%; 15 | border: 2px solid rgb(194, 194, 194); 16 | height: 65px; 17 | transition: all .3s; 18 | &:hover{ 19 | transform: scale(1.08); 20 | } 21 | } 22 | &__info{ 23 | margin: 0 2rem; 24 | font-size: 1.6rem; 25 | .title{ 26 | color: white; 27 | display: block; 28 | } 29 | .artist{ 30 | color: silver; 31 | } 32 | } 33 | } 34 | } 35 | 36 | .rank{ 37 | margin-left:auto; 38 | margin-right: 3rem; 39 | .fav{ 40 | color: rgb(170, 170, 170); 41 | font-size: 1.8rem !important; 42 | &:hover{ 43 | cursor: pointer; 44 | color: #DB1D40; 45 | } 46 | } 47 | .liked{ 48 | color: #DB1D40 !important; 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/sass/blocks/_layout.scss: -------------------------------------------------------------------------------- 1 | .container{ 2 | height: 100vh; 3 | width: 100%; 4 | display: grid; 5 | grid-template-columns: .5fr 350px minmax(700px,800px) 350px .5fr; 6 | grid-template-rows: 1fr 750px 1fr; 7 | } 8 | 9 | .menu{ 10 | grid-row: 2/3; 11 | grid-column: 2/3; 12 | position: relative; 13 | background: #7E2236; 14 | padding: 3rem 4rem; 15 | border-top-left-radius: 5px; 16 | border-bottom-left-radius: 5px; 17 | } 18 | 19 | .main{ 20 | position: relative; 21 | grid-row: 2/3; 22 | grid-column: 3/4; 23 | background: #232323; 24 | overflow-y: auto; 25 | overflow-x: hidden; 26 | padding: 3rem 4rem; 27 | &::-webkit-scrollbar-track { 28 | box-shadow: inset 0 0 8px #00000033; 29 | } 30 | &::-webkit-scrollbar { 31 | width: 3px; 32 | background-color:#303030; 33 | } 34 | &::-webkit-scrollbar-thumb { 35 | background: #c9c9c9; 36 | } 37 | 38 | } 39 | 40 | .cue{ 41 | grid-row: 2/3; 42 | grid-column: 4/5; 43 | background: #DB1D40; 44 | overflow: hidden; 45 | padding: 3rem 4rem; 46 | border-top-right-radius: 5px; 47 | border-bottom-right-radius: 5px; 48 | } 49 | 50 | .red{ 51 | grid-row: 1/-1; 52 | grid-column: 1/4; 53 | background: #DB1D40; 54 | } 55 | 56 | .black{ 57 | grid-row: 1/-1; 58 | grid-column: 4/-1; 59 | background: #232323; 60 | } 61 | 62 | .work{ 63 | position: absolute; 64 | top: 40%; 65 | left: 50%; 66 | text-align: center; 67 | transform: translate(-50%,-50%); 68 | img{ 69 | height: 80px; 70 | } 71 | h3{ 72 | color: silver; 73 | font-size: 1.4rem; 74 | text-transform: uppercase; 75 | } 76 | } -------------------------------------------------------------------------------- /src/sass/blocks/_likes.scss: -------------------------------------------------------------------------------- 1 | .loved{ 2 | margin-top: 2rem; 3 | &__list{ 4 | list-style: none; 5 | display: flex; 6 | flex-wrap: wrap; 7 | } 8 | &__info{ 9 | font-size: 1.6rem; 10 | margin: 0 2rem; 11 | .title{ 12 | display: block; 13 | } 14 | .artist{ 15 | color: silver; 16 | } 17 | } 18 | 19 | &__item{ 20 | display: flex; 21 | align-items: center; 22 | margin: 1.5rem 1rem; 23 | width: calc(100%/2 - 2rem); 24 | 25 | .image{ 26 | height: 120px; 27 | // height: 150px; 28 | transition: all .3s; 29 | border: 1px solid rgb(14, 14, 14); 30 | background: rgb(223, 223, 223); 31 | padding: .3rem; 32 | &:hover{ 33 | cursor: pointer; 34 | transform: translateY(-3px); 35 | } 36 | // border-radius: 50%; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/sass/blocks/_loader.scss: -------------------------------------------------------------------------------- 1 | .signal { 2 | border: 5px solid #DB1D40; 3 | border-radius: 30px; 4 | height: 50px; 5 | left: 50%; 6 | // opacity: 0; 7 | position: absolute; 8 | top: 50%; 9 | width: 50px; 10 | 11 | animation: pulsate 1s ease-out; 12 | animation-iteration-count: infinite; 13 | } 14 | 15 | @keyframes pulsate { 16 | 0% { 17 | transform: scale(.1); 18 | opacity: 0.0; 19 | } 20 | 50% { 21 | opacity: 1; 22 | } 23 | 100% { 24 | transform: scale(1.2); 25 | opacity: 0; 26 | } 27 | } -------------------------------------------------------------------------------- /src/sass/blocks/_main.scss: -------------------------------------------------------------------------------- 1 | .header{ 2 | display: flex; 3 | align-items: center; 4 | &__label{ 5 | text-transform: uppercase; 6 | color: #F0E6E8; 7 | font-weight: 400; 8 | padding: .25rem 0; 9 | font-size: 2rem; 10 | letter-spacing: .1rem; 11 | } 12 | } 13 | 14 | .genres{ 15 | margin: 3rem -.8rem; 16 | &__list{ 17 | list-style: none; 18 | display: flex; 19 | flex-wrap: wrap; 20 | align-items: flex-start; 21 | 22 | &__item{ 23 | font-size: 1.5rem; 24 | font-weight: 700; 25 | text-transform: capitalize; 26 | color: #686868; 27 | margin: 0 1rem; 28 | transition: all .2s; 29 | &:hover{ 30 | color: white; 31 | } 32 | } 33 | 34 | &__link{ 35 | cursor: pointer; 36 | } 37 | } 38 | } 39 | 40 | .active{ 41 | color: #DB1D40; 42 | font-weight: 700; 43 | &:hover{ 44 | color: #DB1D40; 45 | } 46 | } -------------------------------------------------------------------------------- /src/sass/blocks/_menu.scss: -------------------------------------------------------------------------------- 1 | .logo{ 2 | display: flex; 3 | align-items: center; 4 | &__label{ 5 | text-transform: uppercase; 6 | font-weight: 700; 7 | font-size: 2.5rem; 8 | letter-spacing: .2rem; 9 | } 10 | 11 | &__img{ 12 | height: 40px; 13 | margin-right: 1.5rem; 14 | } 15 | } 16 | 17 | .nav{ 18 | margin: 5rem 0; 19 | 20 | &__label{ 21 | margin: 1rem 0; 22 | font-size: 1.6rem; 23 | } 24 | 25 | &__list{ 26 | &__item{ 27 | padding-left: 1.3rem; 28 | backface-visibility: hidden; 29 | display: flex; 30 | align-items: center; 31 | font-size: 1.4rem; 32 | margin: 2rem 0; 33 | color: #F0E6E8; 34 | cursor: pointer; 35 | &:hover{ 36 | color: white; 37 | } 38 | } 39 | 40 | &__link{ 41 | text-decoration: none; 42 | color: inherit; 43 | } 44 | } 45 | } 46 | 47 | 48 | 49 | 50 | .fa{ 51 | font-size: 1rem !important; 52 | margin-right:.8rem; 53 | &__big{ 54 | color: #F0E6E8; 55 | margin-left: 1rem; 56 | font-size: 1.7rem !important; 57 | } 58 | } 59 | 60 | .router-link-exact-active{ 61 | padding-left: .9rem; 62 | color: white; 63 | border-left: 4px solid #DB1D40; 64 | } 65 | .premium{ 66 | color: #FDAB00; 67 | } 68 | 69 | .watermark{ 70 | display: flex; 71 | align-items: center; 72 | flex-direction: column; 73 | justify-content: center; 74 | position: absolute; 75 | bottom: 2rem; 76 | left: 50%; 77 | transform: translateX(-50%); 78 | img{ 79 | margin: 0 1rem; 80 | height: 50px; 81 | } 82 | &__label{ 83 | text-decoration: none; 84 | cursor: pointer; 85 | font-size: 1.8rem; 86 | display: flex; 87 | align-items: center; 88 | justify-content: center; 89 | color: #d8bfd8; 90 | } 91 | &__icon{ 92 | font-size: 2rem !important; 93 | margin-left:1rem; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/sass/blocks/_search.scss: -------------------------------------------------------------------------------- 1 | .search{ 2 | width: 100%; 3 | height: 100%; 4 | display: block; 5 | position: relative; 6 | animation-duration: .5s; 7 | } 8 | .search__form { 9 | width: min-content; 10 | color: white; 11 | background-color: rgb(65, 65, 65); 12 | border-radius: 10rem; 13 | display: flex; 14 | margin: 0 auto; 15 | align-items: center; 16 | padding-left: 3rem; 17 | transition: all .3s; 18 | 19 | 20 | &__field { 21 | background: none; 22 | border: none; 23 | padding: .8rem 0; 24 | font-family: inherit; 25 | color: inherit; 26 | font-size: 1.7rem; 27 | width: 30rem; 28 | transition: all .3s; 29 | 30 | &:focus-within{ 31 | width: 40rem; 32 | } 33 | 34 | &:focus{ 35 | outline: none; 36 | } 37 | &::placeholder{ 38 | color: rgb(187, 187, 187); 39 | font-size: 1.5rem; 40 | } 41 | } 42 | } 43 | .res__header{ 44 | margin:5rem 0; 45 | margin-bottom: 2rem; 46 | &__label{ 47 | font-size:1.8rem; 48 | } 49 | } 50 | 51 | .search__results{ 52 | display: flex; 53 | align-items: center; 54 | flex-wrap: wrap; 55 | width: 100%; 56 | .res{ 57 | align-items: center; 58 | margin: 1.5rem 1rem; 59 | width: calc(100%/3 - 2rem); 60 | display: flex; 61 | 62 | &__info{ 63 | margin: 0 2rem; 64 | font-size: 1.4rem; 65 | .title{ 66 | display: block; 67 | } 68 | .artist{ 69 | color: silver; 70 | } 71 | } 72 | } 73 | } 74 | 75 | .placeholder { 76 | transition: all .5s; 77 | width: 56px; 78 | height: 54px; 79 | border-radius: 50%; 80 | background-repeat: no-repeat; 81 | background-size: contain; 82 | background-image: linear-gradient( 83 | to bottom, 84 | rgba(#162229, .75), 85 | rgba(#DB1D40, .75), 86 | rgba(#6f868c, .75), 87 | rgba(#7E2236, .75) 88 | ); 89 | 90 | .image{ 91 | backface-visibility: hidden; 92 | height: 56px; 93 | width: 56px; 94 | border-radius: 50%; 95 | border: 2px solid rgb(194, 194, 194); 96 | transition: all .5s; 97 | &:hover{ 98 | cursor: pointer; 99 | transform: translateY(-2px); 100 | } 101 | } 102 | 103 | .image__feed{ 104 | width: 65px; 105 | height: 65px; 106 | } 107 | } 108 | 109 | .placeholder__feed{ 110 | width: 65px; 111 | height: 63px; 112 | } -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Datlyfe/ng-tunein/2dd4b9644af38e6f71c9c1228b83ba386ef9c4c0/src/styles.scss -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------