├── .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 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
--------------------------------------------------------------------------------
/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 |
4 |
5 |
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 |
3 |

4 |
TuneIn
5 |
6 |
7 |
24 |
25 |
43 |
44 |
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 |
5 |
6 |
9 |
10 |
11 |
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 |
--------------------------------------------------------------------------------