Commit 8a2d19b8 authored by 神楽坂玲奈's avatar 神楽坂玲奈

OnPush

parent b4b3c595
{
"name": "mycard-mobile",
"version": "1.0.23",
"version": "1.0.29",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -122,9 +122,9 @@
}
},
"@angular/cdk": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.2.1.tgz",
"integrity": "sha512-uwW4eIGJKqOkR+ew6YcEAh1J4SP98jdyDpsZ4IEMkV9+jXcKfcwcxGFpZvs9wJsAvAr8EgNmZ8h+iuZLwJsvmA==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.3.0.tgz",
"integrity": "sha512-L1NM2hU8xvb35mDKIw+SR9UZGZtbSQl6fmd9X/tedaNYjIvyQ7SIX7iDJ+LqD0xUQopB+SeuhDL7D6IwGMV2bQ==",
"requires": {
"tslib": "^1.7.1"
}
......@@ -358,9 +358,9 @@
"dev": true
},
"@angular/material": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-6.2.1.tgz",
"integrity": "sha512-SBoUXxHknkgwzp5pNDHW0jyrTM0d0Tk4lVyDbtEX8VEPtXqG5nL3BSgyjpJbTvqlmy2kOooUu3qgAmt87VH9lw==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-6.3.0.tgz",
"integrity": "sha512-asSzdfLkDDbQEyM/GKUxrlvuj8OvO5DqrNJ3e3uvi0OmZDpaO50yubQvrJ26nbqgwgRo+qwiGNN6XcFwYTRVPQ==",
"requires": {
"tslib": "^1.7.1"
}
......@@ -434,6 +434,13 @@
"@angular-devkit/core": "0.6.8",
"@angular-devkit/schematics": "0.6.8",
"typescript": ">=2.6.2 <2.8"
},
"dependencies": {
"typescript": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
"integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw=="
}
}
},
"@schematics/update": {
......@@ -451,15 +458,15 @@
}
},
"@types/lodash": {
"version": "4.14.109",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.109.tgz",
"integrity": "sha512-hop8SdPUEzbcJm6aTsmuwjIYQo1tqLseKCM+s2bBqTU2gErwI4fE+aqUVOlscPSQbKHKgtMMPoC+h4AIGOJYvw==",
"version": "4.14.110",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.110.tgz",
"integrity": "sha512-iXYLa6olt4tnsCA+ZXeP6eEW3tk1SulWeYyP/yooWfAtXjozqXgtX4+XUtMuOCfYjKGz3F34++qUc3Q+TJuIIw==",
"dev": true
},
"@types/node": {
"version": "10.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.3.tgz",
"integrity": "sha512-/gwCgiI2e9RzzZTKbl+am3vgNqOt7a9fJ/uxv4SqYKxenoEDNVU3KZEadlpusWhQI0A0dOrZ0T68JYKVjzmgdQ==",
"version": "10.3.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.4.tgz",
"integrity": "sha512-YMLlzdeNnAyLrQew39IFRkMacAR5BqKGIEei9ZjdHsIZtv+ZWKYTu1i7QJhetxQ9ReXx8w5f+cixdHZG3zgMQA==",
"dev": true
},
"@webassemblyjs/ast": {
......@@ -8221,9 +8228,9 @@
"integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
},
"tslint": {
"version": "5.9.1",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz",
"integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=",
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz",
"integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
"dev": true,
"requires": {
"babel-code-frame": "^6.22.0",
......@@ -8312,7 +8319,8 @@
"typescript": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
"integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw=="
"integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
"dev": true
},
"uglify-js": {
"version": "3.3.28",
......
{
"name": "mycard-mobile",
"version": "1.0.28",
"version": "1.0.30",
"license": "UNLISENCED",
"scripts": {
"ng": "ng",
......@@ -14,13 +14,13 @@
"private": true,
"dependencies": {
"@angular/animations": "6.0.5",
"@angular/cdk": "^6.2.1",
"@angular/cdk": "^6.3.0",
"@angular/common": "6.0.5",
"@angular/compiler": "6.0.5",
"@angular/core": "6.0.5",
"@angular/forms": "6.0.5",
"@angular/http": "6.0.5",
"@angular/material": "^6.2.1",
"@angular/material": "^6.3.0",
"@angular/platform-browser": "6.0.5",
"@angular/platform-browser-dynamic": "6.0.5",
"@angular/pwa": "^0.6.8",
......@@ -41,11 +41,11 @@
"@angular/cli": "^6.0.8",
"@angular/compiler-cli": "6.0.5",
"@angular/language-service": "6.0.5",
"@types/lodash": "^4.14.109",
"@types/node": "^10.3.3",
"@types/lodash": "^4.14.110",
"@types/node": "^10.3.4",
"codelyzer": "^4.0.1",
"prettier": "^1.12.1",
"tslint": "~5.9.1",
"typescript": "2.7.2"
"tslint": "~5.10.0",
"typescript": "^2.7.2"
}
}
......@@ -27,9 +27,9 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LobbyComponent } from './lobby/lobby.component';
import { MatchDialogComponent } from './match/match.component';
import { MatchDialogComponent } from './match-dialog/match-dialog.component';
import { NewRoomComponent } from './new-room/new-room.component';
import { ResultDialogComponent } from './result/result.dialog';
import { ResultDialogComponent } from './result-dialog/result-dialog.component';
import { RoomListComponent } from './room-list/room-list.component';
import { StorageService } from './storage.service';
import { ToolbarComponent } from './toolbar/toolbar.component';
......
import { Component, Inject, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
@Component({
selector: 'app-confirm-dialog',
templateUrl: './confirm-dialog.component.html',
styleUrls: ['./confirm-dialog.component.css']
styleUrls: ['./confirm-dialog.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ConfirmDialogComponent implements OnInit {
constructor(public dialogRef: MatDialogRef<ConfirmDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {}
......
......@@ -13,7 +13,7 @@
</mat-option>
</mat-autocomplete>
</form>
<button mat-icon-button *ngIf="storage.working">
<button mat-icon-button *ngIf="storage.working | async">
<mat-icon class="fa-spin">sync</mat-icon>
</button>
<a href="https://accounts.moecube.com/profiles" target="_blank" mat-icon-button>
......@@ -84,83 +84,83 @@
<!--<br>轮抽 (开发中)</a></mat-grid-tile>-->
</mat-grid-list>
<mat-card *ngIf="ygopro.points">
<mat-card *ngIf="ygopro.points | async as points">
<mat-grid-list id="points" cols="4" rowHeight="20px">
<mat-grid-tile>
<dt>竞技排名</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.arena_rank}}</dd>
<dd>{{points.arena_rank}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>娱乐排名</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.exp_rank}}</dd>
<dd>{{points.exp_rank}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>竞技胜率</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.athletic_wl_ratio}}%</dd>
<dd>{{points.athletic_wl_ratio}}%</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>经验</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.exp}}</dd>
<dd>{{points.exp}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>胜场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.athletic_win}}</dd>
<dd>{{points.athletic_win}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>胜场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.entertain_win}}</dd>
<dd>{{points.entertain_win}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>负场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.athletic_lose}}</dd>
<dd>{{points.athletic_lose}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>负场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.entertain_lose}}</dd>
<dd>{{points.entertain_lose}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>平局</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.athletic_draw}}</dd>
<dd>{{points.athletic_draw}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>平局</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.entertain_draw}}</dd>
<dd>{{points.entertain_draw}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>总场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.athletic_all}}</dd>
<dd>{{points.athletic_all}}</dd>
</mat-grid-tile>
<mat-grid-tile>
<dt>总场</dt>
</mat-grid-tile>
<mat-grid-tile>
<dd>{{ygopro.points.entertain_all}}</dd>
<dd>{{points.entertain_all}}</dd>
</mat-grid-tile>
</mat-grid-list>
</mat-card>
<mat-card *ngFor="let item of ygopro.news" class="example-card">
<mat-card *ngFor="let item of ygopro.news | async" class="example-card">
<a [href]="item.url" target="_blank">
<mat-card-header>
<img *ngIf="item.image" mat-card-avatar [src]="item.image">
......
import { Component, HostBinding } from '@angular/core';
import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core';
import { FormControl } from '@angular/forms';
import { environment } from '../../environments/environment';
import { LoginService } from '../login.service';
......@@ -13,7 +13,8 @@ import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/internal/oper
selector: 'app-lobby',
templateUrl: 'lobby.component.html',
styleUrls: ['lobby.component.css'],
animations: routerTransition
animations: routerTransition,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LobbyComponent {
@HostBinding('@routerTransition2') animation;
......
......@@ -6,6 +6,7 @@ mat-icon {
mat-dialog-content {
display: flex;
overflow: hidden;
}
mat-dialog-actions {
justify-content: flex-end
......
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { Subscription, timer } from 'rxjs';
import { LoginService } from '../login.service';
......@@ -11,8 +11,9 @@ const offset = new Date().getTimezoneOffset() * 60 * second;
@Component({
selector: 'app-match',
templateUrl: './match.component.html',
styleUrls: ['./match.component.css']
templateUrl: './match-dialog.component.html',
styleUrls: ['./match-dialog.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MatchDialogComponent implements OnInit, OnDestroy {
expect_wait = this.http
......
......@@ -20,10 +20,6 @@ form {
/*margin: 0 10px;*/
/*}*/
mat-select {
padding: 1.2734375em 0;
}
#actions {
text-align: right;
}
......@@ -19,17 +19,21 @@
</button>
<mat-hint align="end">把这个分享给你的朋友</mat-hint>
</mat-form-field>
<mat-select class="full-width" placeholder="卡片允许" name="rule" [(ngModel)]="room.options.rule" required>
<mat-option [value]="0">OCG</mat-option>
<mat-option [value]="1">TCG</mat-option>
<mat-option [value]="2">OCG & TCG</mat-option>
<mat-option [value]="3">专有卡禁止</mat-option>
</mat-select>
<mat-select class="full-width" placeholder="决斗模式" name="mode" [(ngModel)]="room.options.mode" (ngModelChange)="set_start_lp()" required>
<mat-option [value]="0">单局模式</mat-option>
<mat-option [value]="1">比赛模式</mat-option>
<mat-option [value]="2">TAG</mat-option>
</mat-select>
<mat-form-field class="full-width">
<mat-select placeholder="卡片允许" name="rule" [(ngModel)]="room.options.rule" required>
<mat-option [value]="0">OCG</mat-option>
<mat-option [value]="1">TCG</mat-option>
<mat-option [value]="2">OCG & TCG</mat-option>
<mat-option [value]="3">专有卡禁止</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="full-width">
<mat-select placeholder="决斗模式" name="mode" [(ngModel)]="room.options.mode" (ngModelChange)="set_start_lp()" required>
<mat-option [value]="0">单局模式</mat-option>
<mat-option [value]="1">比赛模式</mat-option>
<mat-option [value]="2">TAG</mat-option>
</mat-select>
</mat-form-field>
<!--<h2>额外选项</h2>-->
<!--<mat-slide-toggle #extra class="example-margin" color="primary">额外选项</mat-slide-toggle>-->
......
import { Component, ElementRef, HostBinding, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, ViewChild } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { LoginService } from '../login.service';
import { routerTransition } from '../router.animations';
......@@ -8,7 +8,8 @@ import { YGOProService } from '../ygopro.service';
selector: 'app-new-room',
templateUrl: 'new-room.component.html',
styleUrls: ['new-room.component.css'],
animations: routerTransition
animations: routerTransition,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NewRoomComponent {
@HostBinding('@routerTransition') animation;
......@@ -25,16 +26,11 @@ export class NewRoomComponent {
constructor(public ygopro: YGOProService, private login: LoginService, private snackBar: MatSnackBar) {}
copy(host_password: string) {
try {
this.hostPasswordInput.nativeElement.select();
if (document.execCommand('copy')) {
this.snackBar.open(`房间密码 ${host_password} 已复制到剪贴板`, undefined, { duration: 3000 });
} else {
console.log('Oops, unable to copy');
}
} catch (error) {
console.log(error);
this.hostPasswordInput.nativeElement.select();
if (document.execCommand('copy')) {
this.snackBar.open(`房间密码 ${host_password} 已复制到剪贴板`, undefined, { duration: 3000 });
} else {
console.log('Oops, unable to copy');
}
}
......
......@@ -19,6 +19,5 @@
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close>关闭</button>
<!-- Can optionally provide a result for the closing dialog. -->
<button mat-button (click)="again()">再来一局</button>
<button mat-button [mat-dialog-close]="true">再来一局</button>
</mat-dialog-actions>
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material';
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material';
import { LoginService } from '../login.service';
import { MatchDialogComponent } from '../match/match.component';
@Component({
selector: 'app-result',
templateUrl: './result.dialog.html',
styleUrls: ['./result.dialog.css']
templateUrl: './result-dialog.component.html',
styleUrls: ['./result-dialog.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ResultDialogComponent {
export class ResultDialogComponent implements OnInit {
result: 'win' | 'lose' | 'draw';
dp: string | undefined;
exp: string | undefined;
firstWin: string | undefined;
constructor(
@Inject(MAT_DIALOG_DATA) public last: any,
public login: LoginService,
public dialog: MatDialog,
private dialogRef: MatDialogRef<MatchDialogComponent>
) {
constructor(@Inject(MAT_DIALOG_DATA) public last: any, public login: LoginService) {}
ngOnInit() {
if (this.last.userscorea === this.last.userscoreb) {
this.result = 'draw';
} else if (this.last.winner === this.login.user.username) {
......@@ -43,8 +40,4 @@ export class ResultDialogComponent {
const result = Math.round(current) - Math.round(ex);
return result ? `${result < 0 ? '' : '+'}${Math.round(result)}` : undefined;
}
again() {
this.dialogRef.close(true);
}
}
......@@ -15,7 +15,7 @@
left: 0;
bottom: 56px;
right: 0;
z-index: 1;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
......
......@@ -2,11 +2,11 @@
<div id="container">
<div class="hint" *ngIf="dataSource.loading">
<div class="hint" *ngIf="dataSource.loading | async">
<mat-spinner></mat-spinner>
</div>
<div class="hint" *ngIf="dataSource.empty">现在没有等待中的游戏,可以自行创建一个房间或者去匹配</div>
<div class="hint" *ngIf="dataSource.error">网络错误</div>
<div class="hint" *ngIf="dataSource.empty | async">现在没有等待中的游戏,可以自行创建一个房间或者去匹配</div>
<div class="hint" *ngIf="dataSource.error | async">网络错误</div>
<mat-table #table [dataSource]="dataSource">
......
import { ChangeDetectorRef, Component, HostBinding, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnInit } from '@angular/core';
import { LoginService } from '../login.service';
import { routerTransition } from '../router.animations';
......@@ -8,7 +8,8 @@ import { RoomListDataSource, YGOProService } from '../ygopro.service';
selector: 'app-room-list',
styleUrls: ['room-list.component.css'],
templateUrl: 'room-list.component.html',
animations: routerTransition
animations: routerTransition,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RoomListComponent implements OnInit {
@HostBinding('@routerTransition') animation;
......
import { Injectable } from '@angular/core';
import { EventEmitter, Injectable } from '@angular/core';
import * as path from 'path';
import * as webdav from 'webdav';
import { LoginService } from './login.service';
......@@ -27,7 +27,7 @@ export class StorageService {
app_id = 'ygopro';
client = webdav('https://api.mycard.moe/storage/', this.login.user.username, this.login.user.external_id.toString());
working = false;
working = new EventEmitter();
constructor(private login: LoginService) {}
......@@ -111,11 +111,11 @@ export class StorageService {
// fixme: 如果远端和本地同时没有,但是 localStorage 里有,要删除
// console.log('sync', 'done');
this.working = false;
this.working.emit(false);
}
async download(local_path: string, remote_path: string, index_path: string, time: number) {
this.working = true;
this.working.emit(true);
// console.log('download', local_path, remote_path, index_path, time);
const data: ArrayBuffer = await this.client.getFileContents(remote_path);
window.ygopro.writeFile(local_path, Buffer.from(data).toString('base64'));
......@@ -125,7 +125,7 @@ export class StorageService {
}
async upload(local_path: string, remote_path: string, index_path: string) {
this.working = true;
this.working.emit(true);
// console.log('upload', local_path, remote_path, index_path);
const data = this.read_local(local_path);
await this.client.putFileContents(remote_path, data);
......@@ -141,13 +141,13 @@ export class StorageService {
}
remove_local(local_path: string, remote_path: string, index_path: string) {
this.working = true;
this.working.emit(true);
window.ygopro.unlink(local_path);
localStorage.removeItem(index_path);
}
async remove_remote(local_path: string, remote_path: string, index_path: string) {
this.working = true;
this.working.emit(true);
await this.client.deleteFile(remote_path);
localStorage.removeItem(index_path);
}
......@@ -157,7 +157,7 @@ export class StorageService {
const remote_path = path.join(root, local_path);
const index_path = '_FILE_' + remote_path;
this.working = true;
this.working.emit(true);
window.ygopro.unlink(local_path);
await this.client.deleteFile(remote_path);
localStorage.removeItem(index_path);
......
import { Component } from '@angular/core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { routerTransition } from '../router.animations';
@Component({
selector: 'app-toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.css'],
animations: [routerTransition]
animations: [routerTransition],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ToolbarComponent {
history = history;
......
......@@ -13,9 +13,9 @@
position: absolute;
top: 0;
left: 0;
bottom: 56px;
bottom: 0;
right: 0;
z-index: 1;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
......@@ -33,4 +33,5 @@
#container {
position: relative;
flex: 1;
}
<app-toolbar>观战</app-toolbar>
<div id="container">
<div class="hint" *ngIf="dataSource.loading">
<div class="hint" *ngIf="dataSource.loading | async">
<mat-spinner></mat-spinner>
</div>
<div class="hint" *ngIf="dataSource.empty">现在没有进行中的游戏</div>
<div class="hint" *ngIf="dataSource.error">网络错误</div>
<div class="hint" *ngIf="dataSource.empty | async">现在没有进行中的游戏</div>
<div class="hint" *ngIf="dataSource.error | async">网络错误</div>
<mat-table #table [dataSource]="dataSource">
......
import { ChangeDetectorRef, Component, HostBinding, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, OnInit } from '@angular/core';
import { LoginService } from '../login.service';
import { routerTransition } from '../router.animations';
import { RoomListDataSource, YGOProService } from '../ygopro.service';
......@@ -7,7 +7,8 @@ import { RoomListDataSource, YGOProService } from '../ygopro.service';
selector: 'app-watch',
templateUrl: './watch.component.html',
styleUrls: ['./watch.component.css'],
animations: routerTransition
animations: routerTransition,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class WatchComponent implements OnInit {
@HostBinding('@routerTransition') animation;
......
......@@ -6,7 +6,7 @@
<mat-icon mat-list-icon>&#xe195;</mat-icon><!-- airplanemode_active -->
<h4 mat-line>随机</h4>
</mat-list-item>
<mat-list-item *ngFor="let windbot of ygopro.windbot" (click)="ygopro.join_windbot(windbot)">
<mat-list-item *ngFor="let windbot of ygopro.windbot | async" (click)="ygopro.join_windbot(windbot)">
<img mat-list-icon [src]="login.avatar(windbot)">
<h4 mat-line>{{windbot}}</h4>
</mat-list-item>
......
import { Component, HostBinding } from '@angular/core';
import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core';
import { LoginService } from '../login.service';
import { routerTransition } from '../router.animations';
import { YGOProService } from '../ygopro.service';
......@@ -7,7 +7,8 @@ import { YGOProService } from '../ygopro.service';
selector: 'app-windbot',
templateUrl: './windbot.component.html',
styleUrls: ['./windbot.component.css'],
animations: routerTransition
animations: routerTransition,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class WindbotComponent {
@HostBinding('@routerTransition') animation;
......
import { DataSource } from '@angular/cdk/collections';
import { Injectable } from '@angular/core';
import { EventEmitter, Injectable } from '@angular/core';
import { MatDialog } from '@angular/material';
import { sortBy } from 'lodash';
import { sample, sortBy } from 'lodash';
import { combineLatest, fromEvent, Observable, of } from 'rxjs';
import { BehaviorSubject, combineLatest, fromEvent, Observable, of } from 'rxjs';
import { LoginService } from './login.service';
import { MatchDialogComponent } from './match/match.component';
import { ResultDialogComponent } from './result/result.dialog';
import { MatchDialogComponent } from './match-dialog/match-dialog.component';
import { ResultDialogComponent } from './result-dialog/result-dialog.component';
import { StorageService } from './storage.service';
import { HttpClient } from '@angular/common/http';
import { catchError, filter, map, scan } from 'rxjs/internal/operators';
import { catchError, filter, map, mergeMap, scan, startWith } from 'rxjs/internal/operators';
import { webSocket } from 'rxjs/webSocket';
export interface User {
......@@ -112,12 +112,10 @@ export interface Points {
@Injectable()
export class YGOProService {
news: News[];
windbot: string[];
topics: Observable<any>;
points: Points;
last_game_at: Date;
news: Promise<News[]>;
topics: Promise<any[]>;
windbot: Promise<string[]>;
points: BehaviorSubject<Points | undefined> = new BehaviorSubject(undefined);
readonly default_options: Options = {
mode: 1,
......@@ -131,7 +129,6 @@ export class YGOProService {
lflist: 0,
time_limit: 180
};
readonly servers: Server[] = [
{
id: 'tiramisu',
......@@ -152,70 +149,77 @@ export class YGOProService {
];
constructor(private login: LoginService, private http: HttpClient, private dialog: MatDialog, private storage: StorageService) {
this.load();
}
async load() {
const apps = await this.http.get<App[]>('https://api.mycard.moe/apps.json').toPromise();
const app = apps.find(_app => _app.id === 'ygopro')!;
this.news = app.news['zh-CN'];
this.windbot = (<YGOProData>app.data).windbot['zh-CN'];
this.topics = this.http.get<TopResponse>('https://ygobbs.com/top/quarterly.json').pipe(
map(data =>
data.topic_list.topics.slice(0, 5).map((topic: any) => ({
...topic,
url: new URL(`/t/${topic.slug}/${topic.id}`, 'https://ygobbs.com').toString(),
image_url: topic.image_url && new URL(topic.image_url, 'https://ygobbs.com').toString()
}))
const app = this.http.get<App[]>('https://api.mycard.moe/apps.json').pipe(map(apps => apps.find(_app => _app.id === 'ygopro')!));
this.news = app.pipe(map(_app => _app.news['zh-CN'])).toPromise();
this.windbot = app.pipe(map(_app => (<YGOProData>_app.data).windbot['zh-CN'])).toPromise();
this.topics = this.http
.get<TopResponse>('https://ygobbs.com/top/quarterly.json')
.pipe(
map(data =>
data.topic_list.topics.slice(0, 5).map((topic: any) => ({
...topic,
url: new URL(`/t/${topic.slug}/${topic.id}`, 'https://ygobbs.com').toString(),
image_url: topic.image_url && new URL(topic.image_url, 'https://ygobbs.com').toString()
}))
)
)
);
this.storage.sync();
this.load_points();
await this.load_result(false);
this.listen_result();
}
async load_result(load_points = true) {
const last = await this.http
.get<{ data: any[] }>('https://mycard.moe/ygopro/api/history', {
params: { username: this.login.user.username, type: '0', page_num: '1' }
})
.pipe(map(data => data.data[0]))
.toPromise();
// 从来没打过
if (!last) {
return;
}
const last_game_at = localStorage.getItem('last_game_at');
localStorage.setItem('last_game_at', last.end_time);
const refresh = fromEvent(document, 'visibilitychange').pipe(
filter(() => document.visibilityState === 'visible'),
startWith(undefined)
);
refresh
.pipe(
mergeMap(() =>
this.http.get<Points>('https://api.mycard.moe/ygopro/arena/user', { params: { username: this.login.user.username } })
)
)
.subscribe(this.points);
refresh
.pipe(
mergeMap(() =>
this.http.get<{ data: any[] }>('https://mycard.moe/ygopro/api/history', {
params: { username: this.login.user.username, type: '0', page_num: '1' }
})
),
map(data => data.data[0])
)
.subscribe(async (last: any) => {
// 从来没打过
if (!last) {
return;
}
const last_game_at = localStorage.getItem('last_game_at');
localStorage.setItem('last_game_at', last.end_time);
// 初次运行
if (!last_game_at) {
return;
}
// 初次运行
if (!last_game_at) {
return;
}
// 无新对局
if (last_game_at === last.end_time) {
return;
}
// 无新对局
if (last_game_at === last.end_time) {
return;
}
// 10分钟内有新对局
if (Date.now() - Date.parse(last.end_time) < 10 * 60 * 1000) {
// console.log(last);
if (load_points) {
this.load_points();
}
const again = await this.dialog
.open(ResultDialogComponent, { data: last })
.afterClosed()
.toPromise();
if (again) {
this.request_match(last.type);
}
}
// 10分钟内有新对局
if (Date.now() - Date.parse(last.end_time) < 10 * 60 * 1000) {
const again = await this.dialog
.open(ResultDialogComponent, { data: last })
.afterClosed()
.toPromise();
if (again) {
this.request_match(last.type);
}
}
});
refresh.subscribe(() => {
this.storage.sync();
});
}
async request_match(arena: string) {
......@@ -228,33 +232,6 @@ export class YGOProService {
}
}
listen_result() {
// 那些兼容性的垃圾事儿
// https://www.html5rocks.com/en/tutorials/pagevisibility/intro/
const hidden = ['hidden', 'webkitHidden', 'mozHidden', 'msHidden', 'oHidden'].find(prop => prop in document);
if (hidden) {
const evtname = hidden.replace(/[H|h]idden/, '') + 'visibilitychange';
fromEvent(document, evtname).subscribe(() => {
if (!document[hidden]) {
this.load_result();
this.storage.sync();
}
});
} else {
fromEvent(window, 'focus').subscribe(() => {
this.load_result();
this.storage.sync();
});
}
}
async load_points() {
this.points = await this.http
.get<Points>('https://api.mycard.moe/ygopro/arena/user', { params: { username: this.login.user.username } })
.toPromise();
}
create_room(room: Room, host_password: string) {
const options_buffer = Buffer.alloc(6);
// 建主密码 https://docs.google.com/document/d/1rvrCGIONua2KeRaYNjKBLqyG9uybs9ZI-AmzZKNftOI/edit
......@@ -331,9 +308,9 @@ export class YGOProService {
this.join(name, this.servers[0]);
}
join_windbot(name?: string) {
async join_windbot(name?: string) {
if (!name) {
name = this.windbot[Math.floor(Math.random() * this.windbot.length)];
name = sample(await this.windbot.toPromise());
}
return this.join('AI#' + name, this.servers[0]);
}
......@@ -422,9 +399,9 @@ type Message =
| { event: 'delete'; data: string };
export class RoomListDataSource extends DataSource<Room> {
loading = true;
empty = false;
error: any;
loading = new EventEmitter();
empty = new EventEmitter();
error = new EventEmitter();
constructor(private servers: Server[], private type = 'waiting') {
super();
......@@ -432,6 +409,7 @@ export class RoomListDataSource extends DataSource<Room> {
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<Room[]> {
this.loading.emit(true);
return combineLatest(
this.servers.map(server => {
const url = new URL(server.url!);
......@@ -473,13 +451,13 @@ export class RoomListDataSource extends DataSource<Room> {
),
// loading、empty、error
filter(rooms => {
this.loading = false;
this.empty = rooms.length === 0;
this.loading.emit(false);
this.empty.emit(rooms.length === 0);
return true;
}),
catchError(error => {
this.loading = false;
this.error = error;
this.loading.emit(false);
this.error.emit(error);
return of([]);
})
);
......
......@@ -45,7 +45,6 @@
// import 'core-js/es7/reflect';
import 'core-js/fn/symbol/async-iterator';
/**
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
......@@ -57,11 +56,11 @@ import 'core-js/fn/symbol/async-iterator';
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
(window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
(window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
(window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
......@@ -70,9 +69,7 @@ import 'core-js/fn/symbol/async-iterator';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
......
......@@ -8,7 +8,7 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2016",
"target": "es2017",
"strict": true,
"noImplicitAny": false,
"strictPropertyInitialization": false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment