Skip to content

Commit 613426c

Browse files
Merge pull request #30 from irohalab/refactor/episode-layout
Add List Layout for episodes list
2 parents 036cca0 + 4ac9cd7 commit 613426c

File tree

9 files changed

+342
-35
lines changed

9 files changed

+342
-35
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mira-ui",
3-
"version": "2.8.2",
3+
"version": "2.9.0",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",

src/app/home/bangumi-detail/bangumi-detail.components.ts

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11

2-
import { fromEvent as observableFromEvent, Observable, Subscription } from 'rxjs';
2+
import { fromEvent as observableFromEvent, Subscription } from 'rxjs';
33

44
import {filter, tap, mergeMap} from 'rxjs/operators';
55
import { ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
66
import { HomeChild, HomeService } from "../home.service";
7-
import { Bangumi, User } from "../../entity";
7+
import { Bangumi, Episode, User } from "../../entity";
88
import { ActivatedRoute } from '@angular/router';
99
import { Title } from '@angular/platform-browser';
10-
import { UserService } from '../../user-service';
10+
import { PersistStorage, UserService } from '../../user-service';
1111
import { ChromeExtensionService, ENABLED_STATUS } from '../../browser-extension/chrome-extension.service';
1212
import { DARK_THEME, DarkThemeService, UIDialog, UIToast, UIToastComponent, UIToastRef } from '@irohalab/deneb-ui';
1313
import { AuthError } from '../../../helpers/error';
1414
import { WatchService } from '../watch.service';
1515
import { environment } from '../../../environments/environment';
1616

17+
const LAYOUT_TYPE: string = 'layout_type';
18+
const LAYOUT_TYPES = {
19+
GRID: 'grid_layout',
20+
LIST: 'list_layout'
21+
}
22+
23+
const SORT_ORDER: string = 'bangumi_detail_eps_sort_order';
1724

1825
@Component({
1926
selector: 'view-bangumi-detail',
@@ -39,6 +46,31 @@ export class BangumiDetail extends HomeChild implements OnInit, OnDestroy {
3946

4047
isDarkTheme: boolean;
4148

49+
layoutType: string;
50+
eLayoutTypes = LAYOUT_TYPES;
51+
sortOrder: 'asc' | 'desc' = 'asc';
52+
53+
private _reversedEpisodeList: Episode[];
54+
get episodeList(): Episode[] {
55+
if (this.sortOrder === 'desc') {
56+
if (!this._reversedEpisodeList) {
57+
let firstUnfinished = -1;
58+
this._reversedEpisodeList = this.bangumi.episodes
59+
.filter((eps, index) => {
60+
if (firstUnfinished === -1 && eps.status !== Episode.STATUS_DOWNLOADED) {
61+
firstUnfinished = index;
62+
return true;
63+
}
64+
return eps.status === Episode.STATUS_DOWNLOADED
65+
})
66+
.reverse();
67+
}
68+
return this._reversedEpisodeList;
69+
} else {
70+
return this.bangumi.episodes;
71+
}
72+
}
73+
4274
constructor(homeService: HomeService,
4375
userService: UserService,
4476
private _darkThemeService: DarkThemeService,
@@ -48,6 +80,7 @@ export class BangumiDetail extends HomeChild implements OnInit, OnDestroy {
4880
private _titleService: Title,
4981
private _changeDetector: ChangeDetectorRef,
5082
private _watchService: WatchService,
83+
private _persistStorage: PersistStorage,
5184
toast: UIToast) {
5285
super(homeService);
5386
this._toastRef = toast.makeText();
@@ -59,6 +92,11 @@ export class BangumiDetail extends HomeChild implements OnInit, OnDestroy {
5992
);
6093
}
6194

95+
toggleSortOrder(): void {
96+
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
97+
this._persistStorage.setItem(SORT_ORDER, this.sortOrder);
98+
}
99+
62100
toggleCover() {
63101
if (this._coverExpanded) {
64102
this.checkViewport();
@@ -78,7 +116,14 @@ export class BangumiDetail extends HomeChild implements OnInit, OnDestroy {
78116
);
79117
}
80118

119+
changeLayoutType(layoutType: string): void {
120+
this.layoutType = layoutType;
121+
this._persistStorage.setItem(LAYOUT_TYPE, layoutType);
122+
}
123+
81124
ngOnInit(): void {
125+
this.layoutType = this._persistStorage.getItem(LAYOUT_TYPE, LAYOUT_TYPES.LIST);
126+
this.sortOrder = this._persistStorage.getItem(SORT_ORDER, 'asc') as 'asc' | 'desc';
82127
this._subscription.add(
83128
this._darkThemeService.themeChange
84129
.subscribe(theme => { this.isDarkTheme = theme === DARK_THEME; })
@@ -104,15 +149,18 @@ export class BangumiDetail extends HomeChild implements OnInit, OnDestroy {
104149
mergeMap(() => {
105150
return this._chromeExtensionService.invokeBangumiMethod('bangumiDetail', [this.bangumi.bgm_id]);
106151
}),)
107-
.subscribe((extraInfo) => {
108-
// console.log(extraInfo);
109-
this.extraInfo = extraInfo;
110-
}, (error) => {
111-
console.log(error);
112-
if (error instanceof AuthError && (error as AuthError).isPermission()) {
113-
this._toastRef.show('没有权限');
114-
} else {
115-
this._toastRef.show(error.message);
152+
.subscribe({
153+
next: (extraInfo) => {
154+
// console.log(extraInfo);
155+
this.extraInfo = extraInfo;
156+
},
157+
error: (error) => {
158+
console.log(error);
159+
if (error instanceof AuthError && (error as AuthError).isPermission()) {
160+
this._toastRef.show('没有权限');
161+
} else {
162+
this._toastRef.show(error.message);
163+
}
116164
}
117165
})
118166
);

src/app/home/bangumi-detail/bangumi-detail.dark.less

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,16 @@
3333
.dark-episode-list() {
3434
.episode-list {
3535
background-color: transparent;
36-
.episode-card {
36+
.episode-layout-button-groups {
37+
> .layout-button {
38+
color: @darkLinkColor;
39+
&:focus,
40+
&:hover {
41+
color: @darkLinkHoverColor;
42+
}
43+
}
44+
}
45+
.episode-card, .episode-list-view-item {
3746
background-color: lighten(@darkBackground, 5%);
3847
box-shadow: 0px 1px 3px 0px @darkBoxShadowColor, 0px 0px 0px 1px @darkBoxShadowColor;
3948
.image-fallback {
@@ -49,6 +58,9 @@
4958
}
5059
.content.unfinished {
5160
color: darken(@darkLinkColor, 10%);
61+
.content-heading {
62+
color: darken(@darkLinkColor, 10%);
63+
}
5264
}
5365
}
5466
}

src/app/home/bangumi-detail/bangumi-detail.html

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,73 @@ <h3 class="bangumi-title name">
4747
</div>
4848
<div class="episode-list">
4949
<h4 class="episode-list-heading">各话列表</h4>
50+
<div class="episode-layout-button-groups">
51+
<a class="layout-button sort-button" [title]="sortOrder === 'desc' ? '最早在前,显示所有' : '最新在前,仅显示可观看'" (click)="toggleSortOrder()">
52+
<i class="ui sort numeric icon" [ngClass]="{up: sortOrder === 'asc', down: sortOrder === 'desc'}"></i>
53+
</a>
54+
<a class="layout-button" title="网格视图" (click)="changeLayoutType(eLayoutTypes.GRID)"><i class="ui th icon"></i></a>
55+
<a class="layout-button" title="列表视图" (click)="changeLayoutType(eLayoutTypes.LIST)"><i class="ui th list icon"></i></a>
56+
</div>
5057
<div class="ui divider"></div>
51-
<div class="ui six doubling cards">
52-
<div class="card episode-card" *ngFor="let episode of bangumi.episodes">
58+
<div *ngIf="layoutType === eLayoutTypes.LIST" class="episode-list-view ui stackable two column grid">
59+
<div class="column" *ngFor="let episode of episodeList">
60+
<div class="ui segment episode-list-view-item">
61+
<div class="image" *ngIf="episode.status!==2">
62+
<div class="image-wrapper">
63+
<div class="image-fallback">
64+
<h4 class="ui icon film icon-holder">
65+
<i class="film icon"></i>
66+
</h4>
67+
</div>
68+
</div>
69+
</div>
70+
<div *ngIf="episode.status!==2" class="content unfinished">
71+
<div class="content-heading">
72+
<span>第{{episode.episode_no}}话&nbsp;</span>
73+
<span *ngIf="episode.name_cn || episode.name">{{episode.name_cn || episode.name}}</span>
74+
</div>
75+
<div class="extra-info">
76+
<span>时长:&nbsp;{{episode.duration}}</span>
77+
<span>更新时间:{{episode.airdate}}</span>
78+
<span class="ui tiny teal label" *ngIf="sortOrder === 'desc' && episode.status !== 2">即将播出</span>
79+
</div>
80+
</div>
81+
<a *ngIf="episode.status===2" class="image" [routerLink]="['/play', episode.id]">
82+
<responsive-image [src]="episode.thumbnail_image.url"
83+
[size]="{
84+
width: '100%',
85+
ratio: 0.5625,
86+
originalWidth: episode.thumbnail_image.width,
87+
originalHeight: episode.thumbnail_image.height}"
88+
[background]="episode.thumbnail_image.dominant_color"></responsive-image>
89+
</a>
90+
<a *ngIf="episode.status===2" class="content" [routerLink]="['/play', episode.id]">
91+
<div class="content-heading">
92+
<span>第{{episode.episode_no}}话</span>
93+
<span *ngIf="episode.name_cn || episode.name">{{episode.name_cn || episode.name}}</span>
94+
</div>
95+
<div class="extra-info">
96+
<span>时长:&nbsp;{{episode.duration}}</span>
97+
<span>更新时间:{{episode.airdate}}</span>
98+
<span class="ui tiny teal label" *ngIf="sortOrder === 'desc' && episode.status !== 2">即将播出</span>
99+
</div>
100+
</a>
101+
<div class="ui bottom attached progress"
102+
*ngIf="episode.watch_progress && episode.watch_progress.percentage"
103+
[ngClass]="{
104+
orange: episode.watch_progress.watch_status === 1,
105+
olive: episode.watch_progress.watch_status === 2,
106+
blue: episode.watch_progress.watch_status === 3,
107+
brown: episode.watch_progress.watch_status === 4,
108+
grey: episode.watch_progress.watch_status === 5
109+
}">
110+
<div class="bar" [style.width]="episode.watch_progress.percentage * 100 + '%'"></div>
111+
</div>
112+
</div>
113+
</div>
114+
</div>
115+
<div *ngIf="layoutType === eLayoutTypes.GRID" class="ui six doubling cards">
116+
<div class="card episode-card" *ngFor="let episode of episodeList">
53117
<div class="image" *ngIf="episode.status!==2">
54118
<div class="image-wrapper">
55119
<div class="image-fallback">
@@ -60,7 +124,7 @@ <h4 class="ui icon film icon-holder">
60124
</div>
61125
</div>
62126
<div *ngIf="episode.status!==2" class="content unfinished">
63-
<span>{{episode.episode_no}}话</span>
127+
<span class="episode-title" *ngIf="episode.name_cn || episode.name">{{episode.name_cn || episode.name}}</span>
64128
</div>
65129
<a *ngIf="episode.status===2" class="image" [routerLink]="['/play', episode.id]">
66130
<responsive-image [src]="episode.thumbnail_image.url"
@@ -71,8 +135,9 @@ <h4 class="ui icon film icon-holder">
71135
originalHeight: episode.thumbnail_image.height}"
72136
[background]="episode.thumbnail_image.dominant_color"></responsive-image>
73137
</a>
138+
<span class="episode-number">{{episode.episode_no}}</span>
74139
<a *ngIf="episode.status===2" class="content" [routerLink]="['/play', episode.id]">
75-
<span>{{episode.episode_no}}话</span>
140+
<span class="episode-title" *ngIf="episode.name_cn || episode.name">{{episode.name_cn || episode.name}}</span>
76141
</a>
77142
<div class="ui bottom attached progress"
78143
*ngIf="episode.watch_progress && episode.watch_progress.percentage"

src/app/home/bangumi-detail/bangumi-detail.less

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,27 @@
164164
margin: 0;
165165
padding: 2rem 0 0 0;
166166
}
167+
168+
.episode-layout-button-groups {
169+
position: absolute;
170+
right: 0.4rem;
171+
top: 2.2rem;
172+
> .sort-button {
173+
margin-right: 1.5em;
174+
}
175+
> .layout-button {
176+
display: inline-block;
177+
cursor: pointer;
178+
color: #4c4c4c;
179+
&:focus,
180+
&:hover {
181+
color: #101010;
182+
}
183+
}
184+
}
167185
}
168186

169-
.episode-card {
187+
.episode-card, .episode-list-view {
170188
overflow: hidden;
171189
.image-wrapper {
172190
display: block;
@@ -210,4 +228,51 @@
210228
min-width: 1rem;
211229
}
212230
}
231+
.episode-number {
232+
position: absolute;
233+
left: 0;
234+
top: 0;
235+
color: #ffffff;
236+
background-color: rgba(0, 0, 0, 0.5);
237+
padding: 0.1em 0.4em;
238+
border-radius: 0 0 0.28571429rem 0 !important;
239+
}
240+
}
241+
242+
.episode-list-view {
243+
@media only screen and (max-width: 767px) {
244+
&.ui.stackable.grid {
245+
margin-left: -1rem !important;
246+
margin-right: -1rem !important;
247+
}
248+
}
249+
.episode-list-view-item {
250+
display: flex;
251+
flex-direction: row;
252+
justify-content: flex-start;
253+
padding: 0;
254+
> .image {
255+
width: 8rem;
256+
flex: 0 0 8rem;
257+
}
258+
> .content {
259+
padding: 0.7em 1em;
260+
.extra-info {
261+
margin-top: 0.2em;
262+
font-size: 0.8rem;
263+
color: #a9a9a9;
264+
> span {
265+
display: inline-block;
266+
}
267+
> span:not(:last-child) {
268+
margin-right: 1.5em;
269+
}
270+
}
271+
}
272+
> .content.unfinished {
273+
.content-heading {
274+
color: #696969;
275+
}
276+
}
277+
}
213278
}

0 commit comments

Comments
 (0)