Skip to content

Commit

Permalink
Browse categories (#31)
Browse files Browse the repository at this point in the history
* feat: categories and category components

* feat: add browse data access and render categories

* feat: refactor loading spinner module

* feat: categories loading spinner

* feat: set up category playlists store

* feat: set exact only for home route

* feat: render category playlists
  • Loading branch information
trungvose authored Apr 4, 2021
1 parent aac5ad0 commit c53f207
Show file tree
Hide file tree
Showing 122 changed files with 1,549 additions and 196 deletions.
2 changes: 1 addition & 1 deletion .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"*.{js,ts,html}": [
"*.{ts,html}": [
"eslint --cache"
]
}
196 changes: 171 additions & 25 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -1112,31 +1112,6 @@
}
}
},
"web-browse-feature": {
"projectType": "library",
"root": "libs/web/browse/feature",
"sourceRoot": "libs/web/browse/feature/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/browse/feature/src/**/*.ts",
"libs/web/browse/feature/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/feature"],
"options": {
"jestConfig": "libs/web/browse/feature/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-shell-ui-user-dropdown": {
"projectType": "library",
"root": "libs/web/shell/ui/user-dropdown",
Expand Down Expand Up @@ -1257,6 +1232,177 @@
}
}
}
},
"web-browse-feature-categories": {
"projectType": "library",
"root": "libs/web/browse/feature/categories",
"sourceRoot": "libs/web/browse/feature/categories/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/browse/feature/categories/src/**/*.ts",
"libs/web/browse/feature/categories/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/feature/categories"],
"options": {
"jestConfig": "libs/web/browse/feature/categories/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-browse-feature-category": {
"projectType": "library",
"root": "libs/web/browse/feature/category",
"sourceRoot": "libs/web/browse/feature/category/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/browse/feature/category/src/**/*.ts",
"libs/web/browse/feature/category/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/feature/category"],
"options": {
"jestConfig": "libs/web/browse/feature/category/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-browse-feature-shell": {
"projectType": "library",
"root": "libs/web/browse/feature/shell",
"sourceRoot": "libs/web/browse/feature/shell/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/browse/feature/shell/src/**/*.ts",
"libs/web/browse/feature/shell/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/feature/shell"],
"options": {
"jestConfig": "libs/web/browse/feature/shell/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-browse-data-access": {
"root": "libs/web/browse/data-access",
"sourceRoot": "libs/web/browse/data-access/src",
"projectType": "library",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/web/browse/data-access/**/*.ts"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/data-access"],
"options": {
"jestConfig": "libs/web/browse/data-access/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-browse-ui-category-cover": {
"projectType": "library",
"root": "libs/web/browse/ui/category-cover",
"sourceRoot": "libs/web/browse/ui/category-cover/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/browse/ui/category-cover/src/**/*.ts",
"libs/web/browse/ui/category-cover/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/browse/ui/category-cover"],
"options": {
"jestConfig": "libs/web/browse/ui/category-cover/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-shared-ui-spinner": {
"projectType": "library",
"root": "libs/web/shared/ui/spinner",
"sourceRoot": "libs/web/shared/ui/spinner/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/shared/ui/spinner/src/**/*.ts",
"libs/web/shared/ui/spinner/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/shared/ui/spinner"],
"options": {
"jestConfig": "libs/web/shared/ui/spinner/jest.config.js",
"passWithNoTests": true
}
}
}
},
"web-shared-ui-playlist-list": {
"projectType": "library",
"root": "libs/web/shared/ui/playlist-list",
"sourceRoot": "libs/web/shared/ui/playlist-list/src",
"prefix": "as",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/web/shared/ui/playlist-list/src/**/*.ts",
"libs/web/shared/ui/playlist-list/src/**/*.html"
]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/web/shared/ui/playlist-list"],
"options": {
"jestConfig": "libs/web/shared/ui/playlist-list/jest.config.js",
"passWithNoTests": true
}
}
}
}
}
}
4 changes: 4 additions & 0 deletions apps/angular-spotify/src/custom/tailwind/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
@apply px-8 pt-4;
}

.text-heading {
@apply text-2xl text-white;
}

.text-description {
display: -webkit-box;
-webkit-line-clamp: 2;
Expand Down
8 changes: 7 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ module.exports = {
'<rootDir>/libs/web/browse/feature',
'<rootDir>/libs/web/shell/ui/user-dropdown',
'<rootDir>/libs/web/shell/ui/social-share',
'<rootDir>/libs/web/shell/ui/album-art-overlay'
'<rootDir>/libs/web/shell/ui/album-art-overlay',
'<rootDir>/libs/web/browse/feature/detail',
'<rootDir>/libs/web/browse/feature/shell',
'<rootDir>/libs/web/browse/data-access',
'<rootDir>/libs/web/browse/ui/category-cover',
'<rootDir>/libs/web/shared/ui/spinner',
'<rootDir>/libs/web/shared/ui/playlist-list'
]
};
3 changes: 3 additions & 0 deletions libs/web/browse/data-access/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@nrwl/web/babel"]
}
21 changes: 21 additions & 0 deletions libs/web/browse/data-access/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"parserOptions": {
"project": ["libs/web/browse/data-access/tsconfig.*?.json"]
},
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
7 changes: 7 additions & 0 deletions libs/web/browse/data-access/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# web-browse-data-access

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test web-browse-data-access` to execute the unit tests via [Jest](https://jestjs.io).
14 changes: 14 additions & 0 deletions libs/web/browse/data-access/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
displayName: 'web-browse-data-access',
preset: '../../../../jest.preset.js',
globals: {
'ts-jest': {
tsConfig: '<rootDir>/tsconfig.spec.json'
}
},
transform: {
'^.+\\.[tj]sx?$': 'ts-jest'
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../../../coverage/libs/web/browse/data-access'
};
1 change: 1 addition & 0 deletions libs/web/browse/data-access/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/store';
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GenericStoreStatus } from '@angular-spotify/web/shared/data-access/models';
import { createAction, props } from '@ngrx/store';

export const loadCategories = createAction(
'[Browse Page]/Load Categories',
props<Record<string, string>>()
);

export const loadCategoriesSuccess = createAction(
'[Browse Page/Load Categories Success',
props<{
categories: SpotifyApi.PagingObject<SpotifyApi.CategoryObject>;
}>()
);

export const setCategoriesState = createAction(
'[Browse Page/Set Categories state status',
props<{
status: GenericStoreStatus;
}>()
);
// TODO: Skip load error action, to integrate with toApiResponse operator
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { loadCategories, loadCategoriesSuccess, setCategoriesState } from './categories.action';
import { switchMap, map, withLatestFrom, filter, tap } from 'rxjs/operators';
import { BrowseApiService } from '@angular-spotify/web/shared/data-access/spotify-api';
import { AuthStore } from '@angular-spotify/web/auth/data-access';
import { getCategories } from './categories.selector';
import { select, Store } from '@ngrx/store';

@Injectable()
export class CategoriesEffect {
loadCategories$ = createEffect(() =>
this.actions$.pipe(
ofType(loadCategories),
withLatestFrom(this.store.pipe(select(getCategories))),
tap(([, categories]) => {
if (categories) {
this.store.dispatch(setCategoriesState({ status: 'success' }));
}
}),
filter(([, data]) => !data),
withLatestFrom(this.authStore.country$),
switchMap(([, country]) =>
this.browseApi
.getAllCategories({
country,
limit: 50
})
.pipe(
map((response) =>
loadCategoriesSuccess({
categories: response
})
)
)
)
)
);

constructor(
private store: Store,
private actions$: Actions,
private browseApi: BrowseApiService,
private authStore: AuthStore
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { GenericState } from '@angular-spotify/web/shared/data-access/models';
import { createReducer, on } from '@ngrx/store';
import { loadCategories, loadCategoriesSuccess, setCategoriesState } from './categories.action';
export const categoriesFeatureKey = 'categories';

export interface CategoriesState
extends GenericState<SpotifyApi.PagingObject<SpotifyApi.CategoryObject>> {
map: Map<string, SpotifyApi.CategoryObject>;
}

const initialState: CategoriesState = {
data: null,
status: 'pending',
error: null,
map: new Map()
};

export const categoriesReducer = createReducer(
initialState,
on(loadCategories, (state) => ({ ...state, status: 'loading' })),
on(loadCategoriesSuccess, (state, { categories }) => {
const { map } = state;
categories.items.forEach((category) => {
map.set(category.id, category);
});
return {
...state,
status: 'success',
data: categories,
map: new Map(map)
};
}),
on(setCategoriesState, (state, { status }) => ({ ...state, status }))
);
Loading

0 comments on commit c53f207

Please sign in to comment.