Commit 4abe323c authored by 神楽坂玲奈's avatar 神楽坂玲奈

Revert "update dependencies"

This reverts commit cd8508f4.
parent a6a5a285
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -4,41 +4,41 @@ ...@@ -4,41 +4,41 @@
"license": "UNLISENCED", "license": "UNLISENCED",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve --base-href /mobile/ --deploy-url /mobile/ --locale zh-CN --output-path mobile --open", "start": "ng serve --base-href /mobile/ --deploy-url /mobile --locale zh-CN --output-path mobile --open",
"build": "ng build --base-href /mobile/ --locale zh-CN --aot --build-optimizer --extract-css --prod", "build": "ng build --base-href /mobile/ --locale zh-CN --aot --build-optimizer --extract-css --prod",
"build:dev": "ng build --base-href /mobile2/ --locale zh-CN --aot --build-optimizer --extract-css", "build:dev": "ng build --base-href /mobile2/ --locale zh-CN --aot --build-optimizer --extract-css",
"lint": "ng lint --type-check --fix" "lint": "ng lint --type-check --fix"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "5.2.10", "@angular/animations": "^4.3.6",
"@angular/cdk": "^5.2.5", "@angular/cdk": "^2.0.0-beta.8",
"@angular/common": "5.2.10", "@angular/common": "^4.3.6",
"@angular/compiler": "5.2.10", "@angular/compiler": "^4.3.6",
"@angular/core": "5.2.10", "@angular/core": "^4.3.6",
"@angular/forms": "5.2.10", "@angular/forms": "^4.3.6",
"@angular/http": "5.2.10", "@angular/http": "^4.3.6",
"@angular/material": "^5.2.5", "@angular/material": "^2.0.0-beta.8",
"@angular/platform-browser": "5.2.10", "@angular/platform-browser": "^4.3.6",
"@angular/platform-browser-dynamic": "5.2.10", "@angular/platform-browser-dynamic": "^4.3.6",
"@angular/router": "5.2.10", "@angular/router": "^4.3.6",
"core-js": "^2.5.5", "core-js": "^2.5.0",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"lodash": "^4.17.10", "lodash": "^4.17.4",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"rxjs": "^5.5.10", "rxjs": "^5.4.3",
"webdav": "^1.5.2", "webdav": "https://github.com/moecube/webdav-client/releases/download/v1.0.1/webdav-1.0.1.tgz",
"zone.js": "^0.8.26" "zone.js": "^0.8.17"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "1.7.4", "@angular/cli": "^1.3.2",
"@angular/compiler-cli": "5.2.10", "@angular/compiler-cli": "^4.3.6",
"@angular/language-service": "5.2.10", "@angular/language-service": "^4.3.6",
"@angular/service-worker": "5.2.10", "@angular/service-worker": "^1.0.0-beta.16",
"@types/lodash": "^4.14.108", "@types/lodash": "^4.14.74",
"@types/node": "^9.6.6", "@types/node": "^8.0.25",
"codelyzer": "^4.3.0", "codelyzer": "~3.1.1",
"tslint": "^5.9.1", "tslint": "^5.6.0",
"typescript": "2.6.2" "typescript": "~2.3.3"
} }
} }
module.exports = {
printWidth: 140,
singleQuote: true
};
import { CdkTableModule } from '@angular/cdk';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http'; import { HttpModule, JsonpModule } from '@angular/http';
import { import {
MatAutocompleteModule, MdAutocompleteModule,
MatButtonModule, MdButtonModule,
MatCardModule, MdCardModule,
MatCheckboxModule, MdCheckboxModule,
MatDialogModule, MdDialogModule,
MatGridListModule, MdGridListModule,
MatIconModule, MdIconModule,
MatInputModule, MdInputModule,
MatListModule, MdListModule,
MatMenuModule, MdMenuModule,
MatProgressSpinnerModule, MdProgressSpinnerModule,
MatSelectModule, MdSelectModule,
MatSlideToggleModule, MdSlideToggleModule,
MatSnackBarModule, MdSnackBarModule,
MatTableModule, MdTableModule,
MatToolbarModule MdToolbarModule
} from '@angular/material'; } from '@angular/material';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
...@@ -55,27 +56,29 @@ import { YGOProService } from './ygopro.service'; ...@@ -55,27 +56,29 @@ import { YGOProService } from './ygopro.service';
HttpModule, HttpModule,
AppRoutingModule, AppRoutingModule,
BrowserAnimationsModule, BrowserAnimationsModule,
MatInputModule, MdInputModule,
MatSelectModule, MdSelectModule,
MatCheckboxModule, MdCheckboxModule,
MatButtonModule, MdButtonModule,
MatSlideToggleModule, MdSlideToggleModule,
MatCardModule, MdCardModule,
MatGridListModule, MdGridListModule,
MatIconModule, MdIconModule,
MatTableModule, MdTableModule,
MatListModule, CdkTableModule,
MatDialogModule, MdListModule,
MatToolbarModule, MdDialogModule,
MatSnackBarModule, MdToolbarModule,
MatAutocompleteModule, MdSnackBarModule,
MdAutocompleteModule,
ReactiveFormsModule, ReactiveFormsModule,
JsonpModule, JsonpModule,
MatMenuModule, MdMenuModule,
MatProgressSpinnerModule MdProgressSpinnerModule
], ],
providers: [YGOProService, StorageService], providers: [YGOProService, StorageService],
bootstrap: [AppComponent], bootstrap: [AppComponent],
entryComponents: [MatchDialogComponent, ResultDialogComponent] entryComponents: [MatchDialogComponent, ResultDialogComponent]
}) })
export class AppModule {} export class AppModule {
}
...@@ -35,33 +35,33 @@ input::placeholder { ...@@ -35,33 +35,33 @@ input::placeholder {
/*color: lightgray;*/ /*color: lightgray;*/
/*}*/ /*}*/
mat-input-container { md-input-container {
width: 100%; width: 100%;
} }
[mat-raised-button] { [md-raised-button] {
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none;
} }
[mat-button], [mat-raised-button] { [md-button], [md-raised-button] {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
a[mat-button], a[mat-raised-button] { a[md-button], a[md-raised-button] {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
mat-card-content { md-card-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
[mat-card-avatar] { [md-card-avatar] {
width: 128px; width: 128px;
height: 72px; height: 72px;
object-fit: cover; object-fit: cover;
......
<!--https://github.com/google/material-design-icons/blob/master/iconfont/codepoints--> <!--https://github.com/google/material-design-icons/blob/master/iconfont/codepoints-->
<mat-toolbar color="primary"> <md-toolbar color="primary">
<button id="menu" mat-icon-button (click)="ygopro.openDrawer()"> <button id="menu" md-icon-button (click)="ygopro.openDrawer()">
<mat-icon>menu</mat-icon> <md-icon>menu</md-icon>
</button> </button>
<form (submit)="search(key)"> <form (submit)="search(key)">
<input type=search placeholder="卡片搜索" name="key" [(ngModel)]="key" matAutocomplete="auto" [formControl]="searchCtrl"> <input type=search placeholder="卡片搜索" name="key" [(ngModel)]="key" mdAutocomplete="auto" [formControl]="searchCtrl">
<mat-autocomplete #auto="matAutocomplete"> <md-autocomplete #auto="mdAutocomplete">
<mat-option *ngFor="let card of suggestion | async" [value]="card" (onSelectionChange)="search(card)"> <md-option *ngFor="let card of suggestion | async" [value]="card" (onSelectionChange)="search(card)">
{{ card }} {{ card }}
</mat-option> </md-option>
</mat-autocomplete> </md-autocomplete>
</form> </form>
<button mat-icon-button *ngIf="storage.working"> <button md-icon-button *ngIf="storage.working">
<mat-icon class="fa-spin">sync</mat-icon> <md-icon class="fa-spin">sync</md-icon>
</button> </button>
<a href="https://accounts.moecube.com/profiles" target="_blank" mat-icon-button> <a href="https://accounts.moecube.com/profiles" target="_blank" md-icon-button>
<img id="avatar" [src]="login.user.avatar_url"> <img id="avatar" [src]="login.user.avatar_url">
</a> </a>
<button mat-icon-button [matMenuTriggerFor]="menu"> <button md-icon-button [mdMenuTriggerFor]="menu">
<mat-icon>add</mat-icon> <md-icon>add</md-icon>
</button> </button>
<mat-menu #menu="matMenu"> <md-menu #menu="mdMenu">
<a [href]="login.logout()" mat-menu-item>切换用户</a> <a [href]="login.logout()" md-menu-item>切换用户</a>
<button mat-menu-item>大厅版本 {{version}}</button> <button md-menu-item>大厅版本 {{version}}</button>
<button mat-menu-item *ngIf="build">应用版本 {{build.version_name}}</button> <button md-menu-item *ngIf="build">应用版本 {{build.version_name}}</button>
</mat-menu> </md-menu>
</mat-toolbar> </md-toolbar>
<mat-grid-list cols="4" rowHeight="100px" gutterSize="0"> <md-grid-list cols="4" rowHeight="100px" gutterSize="0">
<!--<mat-grid-tile>--> <!--<md-grid-tile>-->
<!--<a href="https://accounts.moecube.com/profiles" id="avatar" mat-raised-button [style.background-image]="'url(' + login.user.avatar_url + ')'">{{login.user.username}}</a>--> <!--<a href="https://accounts.moecube.com/profiles" id="avatar" md-raised-button [style.background-image]="'url(' + login.user.avatar_url + ')'">{{login.user.username}}</a>-->
<!--</mat-grid-tile>--> <!--</md-grid-tile>-->
<mat-grid-tile> <md-grid-tile>
<button mat-raised-button color="primary" (click)="ygopro.request_match('athletic')"> <button md-raised-button color="primary" (click)="ygopro.request_match('athletic')">
<mat-icon fontSet="fa" fontIcon="fa-futbol-o"></mat-icon> <md-icon fontSet="fa" fontIcon="fa-futbol-o"></md-icon>
<br>竞技匹配 <br>竞技匹配
</button> </button>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<button mat-raised-button color="primary" (click)="ygopro.request_match('entertain')"> <button md-raised-button color="primary" (click)="ygopro.request_match('entertain')">
<mat-icon>toys</mat-icon> <md-icon>toys</md-icon>
<br>娱乐匹配 <br>娱乐匹配
</button> </button>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile><a mat-raised-button color="primary" routerLink="/ygopro/rooms"> <md-grid-tile><a md-raised-button color="primary" routerLink="/ygopro/rooms">
<mat-icon>games</mat-icon> <md-icon>games</md-icon>
<br>房间列表</a></mat-grid-tile> <br>房间列表</a></md-grid-tile>
<mat-grid-tile><a mat-raised-button color="primary" routerLink="/ygopro/rooms/new"> <md-grid-tile><a md-raised-button color="primary" routerLink="/ygopro/rooms/new">
<mat-icon>&#xe146;</mat-icon><!--add_box--> <md-icon>&#xe146;</md-icon><!--add_box-->
<br>创建房间</a></mat-grid-tile> <br>创建房间</a></md-grid-tile>
<mat-grid-tile><a mat-raised-button routerLink="/ygopro/windbot"> <md-grid-tile><a md-raised-button routerLink="/ygopro/windbot">
<mat-icon>&#xe195;</mat-icon><!-- airplanemode_active --> <md-icon>&#xe195;</md-icon><!-- airplanemode_active -->
<br>单人模式</a></mat-grid-tile> <br>单人模式</a></md-grid-tile>
<mat-grid-tile><a mat-raised-button routerLink="/ygopro/watch"> <md-grid-tile><a md-raised-button routerLink="/ygopro/watch">
<mat-icon>&#xe417;</mat-icon><!--remove_red_eye--> <md-icon>&#xe417;</md-icon><!--remove_red_eye-->
<br>观战</a></mat-grid-tile> <br>观战</a></md-grid-tile>
<!--<mat-grid-tile>--> <!--<md-grid-tile>-->
<!--<button mat-raised-button (click)="ygopro.watch_replay()">--> <!--<button md-raised-button (click)="ygopro.watch_replay()">-->
<!--<mat-icon>history</mat-icon>--> <!--<md-icon>history</md-icon>-->
<!--<br>观看录像--> <!--<br>观看录像-->
<!--</button>--> <!--</button>-->
<!--</mat-grid-tile>--> <!--</md-grid-tile>-->
<mat-grid-tile> <md-grid-tile>
<button mat-raised-button (click)="ygopro.edit_deck()"> <button md-raised-button (click)="ygopro.edit_deck()">
<mat-icon>edit</mat-icon> <md-icon>edit</md-icon>
<br>编辑卡组 <br>编辑卡组
</button> </button>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile><a mat-raised-button href="https://ygobbs.com" target="_blank"> <md-grid-tile><a md-raised-button href="https://ygobbs.com" target="_blank">
<mat-icon>forum</mat-icon> <md-icon>forum</md-icon>
<br>社区</a></mat-grid-tile> <br>社区</a></md-grid-tile>
<mat-grid-tile><a mat-raised-button [href]="arena_url" target="_blank"> <md-grid-tile><a md-raised-button [href]="arena_url" target="_blank">
<mat-icon fontSet="fa" fontIcon="fa-trophy"></mat-icon> <md-icon fontSet="fa" fontIcon="fa-trophy"></md-icon>
<br>决斗数据库</a></mat-grid-tile> <br>决斗数据库</a></md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<button mat-raised-button (click)="ygopro.backHome()"><span class="icon">233</span><br>直连</button> <button md-raised-button (click)="ygopro.backHome()"><span class="icon">233</span><br>直连</button>
</mat-grid-tile> </md-grid-tile>
</mat-grid-list> </md-grid-list>
<mat-card *ngIf="ygopro.points"> <md-card *ngIf="ygopro.points">
<mat-grid-list id="points" cols="4" rowHeight="20px"> <md-grid-list id="points" cols="4" rowHeight="20px">
<mat-grid-tile> <md-grid-tile>
<dt>竞技排名</dt> <dt>竞技排名</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.arena_rank}}</dd> <dd>{{ygopro.points.arena_rank}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>娱乐排名</dt> <dt>娱乐排名</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.exp_rank}}</dd> <dd>{{ygopro.points.exp_rank}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>竞技胜率</dt> <dt>竞技胜率</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.athletic_wl_ratio}}%</dd> <dd>{{ygopro.points.athletic_wl_ratio}}%</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>经验</dt> <dt>经验</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.exp}}</dd> <dd>{{ygopro.points.exp}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>胜场</dt> <dt>胜场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.athletic_win}}</dd> <dd>{{ygopro.points.athletic_win}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>胜场</dt> <dt>胜场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.entertain_win}}</dd> <dd>{{ygopro.points.entertain_win}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>负场</dt> <dt>负场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.athletic_lose}}</dd> <dd>{{ygopro.points.athletic_lose}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>负场</dt> <dt>负场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.entertain_lose}}</dd> <dd>{{ygopro.points.entertain_lose}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>平局</dt> <dt>平局</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.athletic_draw}}</dd> <dd>{{ygopro.points.athletic_draw}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>平局</dt> <dt>平局</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.entertain_draw}}</dd> <dd>{{ygopro.points.entertain_draw}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>总场</dt> <dt>总场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.athletic_all}}</dd> <dd>{{ygopro.points.athletic_all}}</dd>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dt>总场</dt> <dt>总场</dt>
</mat-grid-tile> </md-grid-tile>
<mat-grid-tile> <md-grid-tile>
<dd>{{ygopro.points.entertain_all}}</dd> <dd>{{ygopro.points.entertain_all}}</dd>
</mat-grid-tile> </md-grid-tile>
</mat-grid-list> </md-grid-list>
</mat-card> </md-card>
<mat-card *ngFor="let item of ygopro.news" class="example-card"> <md-card *ngFor="let item of ygopro.news" class="example-card">
<a [href]="item.url" target="_blank"> <a [href]="item.url" target="_blank">
<mat-card-header> <md-card-header>
<img *ngIf="item.image" mat-card-avatar [src]="item.image"> <img *ngIf="item.image" md-card-avatar [src]="item.image">
<mat-card-title>{{item.title}}</mat-card-title> <md-card-title>{{item.title}}</md-card-title>
<mat-card-subtitle>{{item.updated_at | date}}</mat-card-subtitle> <md-card-subtitle>{{item.updated_at | date}}</md-card-subtitle>
</mat-card-header> </md-card-header>
</a> </a>
</mat-card> </md-card>
<mat-card *ngFor="let item of ygopro.topics | async"> <md-card *ngFor="let item of ygopro.topics | async">
<a [href]="item.url" target="_blank"> <a [href]="item.url" target="_blank">
<mat-card-header> <md-card-header>
<img *ngIf="item.image_url" mat-card-avatar [src]="item.image_url"> <img *ngIf="item.image_url" md-card-avatar [src]="item.image_url">
<mat-card-title>{{item.title}}</mat-card-title> <md-card-title>{{item.title}}</md-card-title>
<mat-card-subtitle>by {{item.last_poster_username}} / {{item.last_posted_at | date}} <md-card-subtitle>by {{item.last_poster_username}} / {{item.last_posted_at | date}}
</mat-card-subtitle> </md-card-subtitle>
</mat-card-header> </md-card-header>
</a> </a>
</mat-card> </md-card>
mat-icon { md-icon {
width: 96px; width: 96px;
height: 96px; height: 96px;
font-size: 96px; font-size: 96px;
} }
mat-dialog-content { md-dialog-content {
display: flex; display: flex;
} }
mat-dialog-actions { md-dialog-actions {
justify-content: flex-end justify-content: flex-end
} }
ul { ul {
......
<h2 mat-dialog-title *ngIf="arena == 'athletic'">竞技匹配</h2> <h2 md-dialog-title *ngIf="arena == 'athletic'">竞技匹配</h2>
<h2 mat-dialog-title *ngIf="arena == 'entertain'">娱乐匹配</h2> <h2 md-dialog-title *ngIf="arena == 'entertain'">娱乐匹配</h2>
<mat-dialog-content> <md-dialog-content>
<mat-icon *ngIf="arena == 'athletic'" fontSet="fa" fontIcon="fa-futbol-o" class="fa-spin"></mat-icon> <md-icon *ngIf="arena == 'athletic'" fontSet="fa" fontIcon="fa-futbol-o" class="fa-spin"></md-icon>
<mat-icon *ngIf="arena == 'entertain'" class="fa-spin">toys</mat-icon> <md-icon *ngIf="arena == 'entertain'" class="fa-spin">toys</md-icon>
<ul> <ul>
<dt>预计等待时间</dt> <dt>预计等待时间</dt>
<dd>{{expect_wait | async | date: 'mm:ss'}}</dd> <dd>{{expect_wait | async | date: 'mm:ss'}}</dd>
<dt>实际等待时间</dt> <dt>实际等待时间</dt>
<dd>{{actual_wait | async | date: 'mm:ss'}}</dd> <dd>{{actual_wait | async | date: 'mm:ss'}}</dd>
</ul> </ul>
</mat-dialog-content> </md-dialog-content>
<mat-dialog-actions> <md-dialog-actions>
<button mat-button mat-dialog-close>取消</button> <button md-button md-dialog-close>取消</button>
</mat-dialog-actions> </md-dialog-actions>
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Headers, Http } from '@angular/http'; import { Headers, Http } from '@angular/http';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs/Subscription';
import { LoginService } from '../login.service'; import { LoginService } from '../login.service';
...@@ -14,39 +14,34 @@ const offset = new Date().getTimezoneOffset() * 60 * second; ...@@ -14,39 +14,34 @@ const offset = new Date().getTimezoneOffset() * 60 * second;
styleUrls: ['./match.component.css'] styleUrls: ['./match.component.css']
}) })
export class MatchDialogComponent implements OnInit, OnDestroy { export class MatchDialogComponent implements OnInit, OnDestroy {
expect_wait = this.http.get('https://api.mycard.moe/ygopro/match/stats/' + this.arena).map(response => response.json() * second + offset); expect_wait = this.http.get('https://api.mycard.moe/ygopro/match/stats/' + this.arena).map(response => response.json() * second + offset);
actual_wait = Observable.timer(0, second).map(timestamp => timestamp * second + offset); actual_wait = Observable.timer(0, second).map(timestamp => timestamp * second + offset);
matching: Subscription; matching: Subscription;
constructor( constructor(@Inject(MD_DIALOG_DATA) public arena: string,
@Inject(MAT_DIALOG_DATA) public arena: string, private dialogRef: MdDialogRef<MatchDialogComponent>,
private dialogRef: MatDialogRef<MatchDialogComponent>,
private http: Http, private http: Http,
private login: LoginService private login: LoginService) {
) {} }
ngOnInit() { ngOnInit() {
this.matching = this.http this.matching = this.http.post('https://api.mycard.moe/ygopro/match', null, {
.post('https://api.mycard.moe/ygopro/match', null, {
headers: new Headers({ headers: new Headers({
Authorization: 'Basic ' + Buffer.from(this.login.user.username + ':' + this.login.user.external_id).toString('base64') Authorization: 'Basic ' + Buffer.from(this.login.user.username + ':' + this.login.user.external_id).toString('base64')
}), }),
params: { arena: this.arena, locale: 'zh-CN' } params: {arena: this.arena, locale: 'zh-CN'}
}) }).map(response => response.json()).subscribe((data) => {
.map(response => response.json())
.subscribe(
data => {
this.dialogRef.close(data); this.dialogRef.close(data);
}, }, (error) => {
error => {
alert(`匹配失败`); alert(`匹配失败`);
this.dialogRef.close(); this.dialogRef.close();
} });
);
} }
ngOnDestroy() { ngOnDestroy() {
this.matching.unsubscribe(); this.matching.unsubscribe();
} }
} }
...@@ -11,7 +11,7 @@ form { ...@@ -11,7 +11,7 @@ form {
/*margin: 0 10px;*/ /*margin: 0 10px;*/
/*}*/ /*}*/
mat-select { md-select {
padding: 1.2734375em 0; padding: 1.2734375em 0;
} }
......
<app-toolbar>创建房间</app-toolbar> <app-toolbar>创建房间</app-toolbar>
<form (submit)="ygopro.create_room(room, host_password)" ngNativeValidate> <form (submit)="ygopro.create_room(room, host_password)" ngNativeValidate>
<mat-input-container *ngIf="!room.private" class="full-width"> <md-input-container *ngIf="!room.private" class="full-width">
<input matInput placeholder="游戏标题" name="title" [(ngModel)]="room.title" minlength="1" maxlength="12" required> <input mdInput placeholder="游戏标题" name="title" [(ngModel)]="room.title" minlength="1" maxlength="12" required>
<mat-hint align="end">{{room.title?.length || 0}} / 12</mat-hint> <md-hint align="end">{{room.title?.length || 0}} / 12</md-hint>
</mat-input-container> </md-input-container>
<mat-input-container *ngIf="room.private" class="full-width"> <md-input-container *ngIf="room.private" class="full-width">
<mat-placeholder> <md-placeholder>
<mat-icon>&#xe0da;</mat-icon><!-- vpn_key --> <md-icon>&#xe0da;</md-icon><!-- vpn_key -->
<span>房间密码</span> <span>房间密码</span>
</mat-placeholder> </md-placeholder>
<input #hostPasswordInput matInput name="title" [(ngModel)]="host_password" readonly> <input #hostPasswordInput mdInput name="title" [(ngModel)]="host_password" readonly>
<button type="button" mat-icon-button matSuffix (click)="copy(host_password)"> <button type="button" md-icon-button mdSuffix (click)="copy(host_password)">
<mat-icon>&#xe14d;</mat-icon><!-- content_copy --> <md-icon>&#xe14d;</md-icon><!-- content_copy -->
</button> </button>
<button type="button" mat-icon-button matSuffix (click)="share(host_password)"> <button type="button" md-icon-button mdSuffix (click)="share(host_password)">
<mat-icon>share</mat-icon> <md-icon>share</md-icon>
</button> </button>
<mat-hint align="end">把这个分享给你的朋友</mat-hint> <md-hint align="end">把这个分享给你的朋友</md-hint>
</mat-input-container> </md-input-container>
<mat-select class="full-width" placeholder="卡片允许" name="rule" [(ngModel)]="room.options.rule" required> <md-select class="full-width" placeholder="卡片允许" name="rule" [(ngModel)]="room.options.rule" required>
<mat-option [value]="0">OCG</mat-option> <md-option [value]="0">OCG</md-option>
<mat-option [value]="1">TCG</mat-option> <md-option [value]="1">TCG</md-option>
<mat-option [value]="2">OCG & TCG</mat-option> <md-option [value]="2">OCG & TCG</md-option>
<mat-option [value]="3">专有卡禁止</mat-option> <md-option [value]="3">专有卡禁止</md-option>
</mat-select> </md-select>
<mat-select class="full-width" placeholder="决斗模式" name="mode" [(ngModel)]="room.options.mode" (ngModelChange)="set_start_lp()" required> <md-select class="full-width" placeholder="决斗模式" name="mode" [(ngModel)]="room.options.mode" (ngModelChange)="set_start_lp()" required>
<mat-option [value]="0">单局模式</mat-option> <md-option [value]="0">单局模式</md-option>
<mat-option [value]="1">比赛模式</mat-option> <md-option [value]="1">比赛模式</md-option>
<mat-option [value]="2">TAG</mat-option> <md-option [value]="2">TAG</md-option>
</mat-select> </md-select>
<!--<h2>额外选项</h2>--> <!--<h2>额外选项</h2>-->
<!--<mat-slide-toggle #extra class="example-margin" color="primary">额外选项</mat-slide-toggle>--> <!--<md-slide-toggle #extra class="example-margin" color="primary">额外选项</md-slide-toggle>-->
<mat-input-container class="full-width"> <md-input-container class="full-width">
<input name="start_lp" [(ngModel)]="room.options.start_lp" matInput type="number" min="1" max="65536" placeholder="初始 LP" required> <input name="start_lp" [(ngModel)]="room.options.start_lp" mdInput type="number" min="1" max="65536" placeholder="初始 LP" required>
</mat-input-container> </md-input-container>
<mat-input-container class="full-width"> <md-input-container class="full-width">
<input name="start_hand" [(ngModel)]="room.options.start_hand" matInput type="number" min="0" max="16" placeholder="初始手牌数" required> <input name="start_hand" [(ngModel)]="room.options.start_hand" mdInput type="number" min="0" max="16" placeholder="初始手牌数" required>
</mat-input-container> </md-input-container>
<mat-input-container class="full-width"> <md-input-container class="full-width">
<input name="draw_count" [(ngModel)]="room.options.draw_count" matInput type="number" min="0" max="16" placeholder="每回合抽卡" required> <input name="draw_count" [(ngModel)]="room.options.draw_count" mdInput type="number" min="0" max="16" placeholder="每回合抽卡" required>
</mat-input-container> </md-input-container>
<mat-checkbox class="full-width" name="room.private" [(ngModel)]="room.private">私密房间</mat-checkbox> <md-checkbox class="full-width" name="room.private" [(ngModel)]="room.private">私密房间</md-checkbox>
<mat-checkbox class="full-width" name="room.enable_priority" [(ngModel)]="room.options.enable_priority">旧规则 <md-checkbox class="full-width" name="room.enable_priority" [(ngModel)]="room.options.enable_priority">旧规则
</mat-checkbox> </md-checkbox>
<mat-checkbox class="full-width" name="room.no_check_deck" [(ngModel)]="room.options.no_check_deck">不检查卡组</mat-checkbox> <md-checkbox class="full-width" name="room.no_check_deck" [(ngModel)]="room.options.no_check_deck">不检查卡组</md-checkbox>
<mat-checkbox class="full-width" name="room.no_shuffle_deck" [(ngModel)]="room.options.no_shuffle_deck">不洗切卡组 <md-checkbox class="full-width" name="room.no_shuffle_deck" [(ngModel)]="room.options.no_shuffle_deck">不洗切卡组
</mat-checkbox> </md-checkbox>
<div id="actions"> <div id="actions">
<!--<button routerLink="/ygopro/lobby" mat-raised-button>返回大厅</button>--> <!--<button routerLink="/ygopro/lobby" md-raised-button>返回大厅</button>-->
<button type="submit" color="primary" mat-raised-button>创建游戏</button> <button type="submit" color="primary" md-raised-button>创建游戏</button>
</div> </div>
</form> </form>
import { Component, ElementRef, HostBinding, ViewChild } from '@angular/core'; import { Component, ElementRef, HostBinding, ViewChild } from '@angular/core';
import { MatSnackBar } from '@angular/material'; import { MdSnackBar } from '@angular/material';
import { LoginService } from '../login.service'; import { LoginService } from '../login.service';
import { routerTransition } from '../router.animations'; import { routerTransition } from '../router.animations';
import { YGOProService } from '../ygopro.service'; import { YGOProService } from '../ygopro.service';
...@@ -27,7 +27,7 @@ export class NewRoomComponent { ...@@ -27,7 +27,7 @@ export class NewRoomComponent {
options: {...this.ygopro.default_options} options: {...this.ygopro.default_options}
}; };
constructor(public ygopro: YGOProService, private login: LoginService, private snackBar: MatSnackBar) { constructor(public ygopro: YGOProService, private login: LoginService, private snackBar: MdSnackBar) {
} }
copy(host_password: string) { copy(host_password: string) {
......
mat-list-item /deep/ .mat-list-item-content { md-list-item /deep/ .mat-list-item-content {
justify-content: space-between; justify-content: space-between;
height: 28px !important; height: 28px !important;
padding: 0 padding: 0
} }
mat-grid-list { md-grid-list {
font-size: 14px; font-size: 14px;
} }
mat-grid-tile dt, mat-grid-tile dd { md-grid-tile dt, md-grid-tile dd {
width: 100%; width: 100%;
} }
mat-grid-tile { md-grid-tile {
text-align: left; text-align: left;
} }
mat-grid-tile dd { md-grid-tile dd {
margin: 0; margin: 0;
text-align: left; text-align: left;
} }
......
<h2 mat-dialog-title *ngIf="result === 'win'">胜利</h2> <h2 md-dialog-title *ngIf="result === 'win'">胜利</h2>
<h2 mat-dialog-title *ngIf="result === 'lose'">败北</h2> <h2 md-dialog-title *ngIf="result === 'lose'">败北</h2>
<h2 mat-dialog-title *ngIf="result === 'draw'">平局</h2> <h2 md-dialog-title *ngIf="result === 'draw'">平局</h2>
<mat-dialog-content> <md-dialog-content>
<mat-list [ngClass]="result" *ngIf="dp || exp || firstWin"> <md-list [ngClass]="result" *ngIf="dp || exp || firstWin">
<mat-list-item *ngIf="dp"> <md-list-item *ngIf="dp">
<dt>D.P</dt> <dt>D.P</dt>
<dd>{{dp}}</dd> <dd>{{dp}}</dd>
</mat-list-item> </md-list-item>
<mat-list-item *ngIf="exp"> <md-list-item *ngIf="exp">
<dt>EXP</dt> <dt>EXP</dt>
<dd>{{exp}}</dd> <dd>{{exp}}</dd>
</mat-list-item> </md-list-item>
<mat-list-item *ngIf="firstWin"> <md-list-item *ngIf="firstWin">
<dt>D.P (首胜)</dt> <dt>D.P (首胜)</dt>
<dd>{{firstWin}}</dd> <dd>{{firstWin}}</dd>
</mat-list-item> </md-list-item>
</mat-list> </md-list>
</mat-dialog-content> </md-dialog-content>
<mat-dialog-actions> <md-dialog-actions>
<button mat-button mat-dialog-close>关闭</button> <button md-button md-dialog-close>关闭</button>
<!-- Can optionally provide a result for the closing dialog. --> <!-- Can optionally provide a result for the closing dialog. -->
<button mat-button (click)="again()">再来一局</button> <button md-button (click)="again()">再来一局</button>
</mat-dialog-actions> </md-dialog-actions>
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material'; import { MD_DIALOG_DATA, MdDialog, MdDialogRef } from '@angular/material';
import { LoginService } from '../login.service'; import { LoginService } from '../login.service';
import { MatchDialogComponent } from '../match/match.component'; import { MatchDialogComponent } from '../match/match.component';
...@@ -15,11 +15,11 @@ export class ResultDialogComponent { ...@@ -15,11 +15,11 @@ export class ResultDialogComponent {
exp: string | undefined; exp: string | undefined;
firstWin: string | undefined; firstWin: string | undefined;
constructor(@Inject(MAT_DIALOG_DATA) public last: any, constructor(@Inject(MD_DIALOG_DATA) public last: any,
public login: LoginService, public login: LoginService,
private http: Http, private http: Http,
public dialog: MatDialog, public dialog: MdDialog,
private dialogRef: MatDialogRef<MatchDialogComponent>) { private dialogRef: MdDialogRef<MatchDialogComponent>) {
if (this.last.userscorea === this.last.userscoreb) { if (this.last.userscorea === this.last.userscoreb) {
this.result = 'draw'; this.result = 'draw';
} else if (this.last.winner === this.login.user.username) { } else if (this.last.winner === this.login.user.username) {
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
flex: 2 flex: 2
} }
mat-table { md-table {
flex: 1; flex: 1;
padding-bottom: 36px; padding-bottom: 36px;
} }
......
<app-toolbar>房间列表</app-toolbar> <app-toolbar>房间列表</app-toolbar>
<div class="hint" *ngIf="dataSource.loading"><mat-spinner></mat-spinner></div> <div class="hint" *ngIf="dataSource.loading"><md-spinner></md-spinner></div>
<div class="hint" *ngIf="dataSource.empty">现在没有等待中的游戏,可以自行创建一个房间或者去匹配</div> <div class="hint" *ngIf="dataSource.empty">现在没有等待中的游戏,可以自行创建一个房间或者去匹配</div>
<div class="hint" *ngIf="dataSource.error">网络错误</div> <div class="hint" *ngIf="dataSource.error">网络错误</div>
<mat-table #table [dataSource]="dataSource"> <md-table #table [dataSource]="dataSource">
<!--- Note that these columns can be defined in any order. <!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on room row definition" --> The actual rendered columns are set as a property on room row definition" -->
<!-- ID Column --> <!-- ID Column -->
<ng-container matColumnDef="title"> <ng-container cdkColumnDef="title">
<mat-header-cell class="game-title" *matHeaderCellDef>游戏标题</mat-header-cell> <md-header-cell class="game-title" *cdkHeaderCellDef>游戏标题</md-header-cell>
<mat-cell class="game-title" *matCellDef="let room">{{room.title}}</mat-cell> <md-cell class="game-title" *cdkCellDef="let room">{{room.title}}</md-cell>
</ng-container> </ng-container>
<!-- Progress Column --> <!-- Progress Column -->
<ng-container matColumnDef="users"> <ng-container cdkColumnDef="users">
<mat-header-cell *matHeaderCellDef>玩家</mat-header-cell> <md-header-cell *cdkHeaderCellDef>玩家</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<img *ngFor="let user of room.users" class="avatar" [src]="login.avatar(user.username)"> <img *ngFor="let user of room.users" class="avatar" [src]="login.avatar(user.username)">
</mat-cell> </md-cell>
</ng-container> </ng-container>
<!-- Name Column --> <!-- Name Column -->
<ng-container matColumnDef="mode"> <ng-container cdkColumnDef="mode">
<mat-header-cell *matHeaderCellDef>决斗模式</mat-header-cell> <md-header-cell *cdkHeaderCellDef>决斗模式</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<span *ngIf="room.options.mode === 0">单局模式</span> <span *ngIf="room.options.mode === 0">单局模式</span>
<span *ngIf="room.options.mode === 1">比赛模式</span> <span *ngIf="room.options.mode === 1">比赛模式</span>
<span *ngIf="room.options.mode === 2">TAG</span> <span *ngIf="room.options.mode === 2">TAG</span>
</mat-cell> </md-cell>
</ng-container> </ng-container>
<!-- Color Column --> <!-- Color Column -->
<ng-container matColumnDef="extra"> <ng-container cdkColumnDef="extra">
<mat-header-cell *matHeaderCellDef>额外选项</mat-header-cell> <md-header-cell *cdkHeaderCellDef>额外选项</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<span *ngIf="room.options.rule != ygopro.default_options.rule">{{{'0': 'OCG', '1': 'TCG', '2': 'O/T', '3': '专有卡禁止'}[room.options.rule]}}</span> <span *ngIf="room.options.rule != ygopro.default_options.rule">{{{'0': 'OCG', '1': 'TCG', '2': 'O/T', '3': '专有卡禁止'}[room.options.rule]}}</span>
<span *ngIf="room.options.start_lp != ygopro.default_options.start_lp">{{room.options.start_lp}} LP</span> <span *ngIf="room.options.start_lp != ygopro.default_options.start_lp">{{room.options.start_lp}} LP</span>
<span *ngIf="room.options.start_hand != ygopro.default_options.start_hand">{{room.options.start_hand}} 初始</span> <span *ngIf="room.options.start_hand != ygopro.default_options.start_hand">{{room.options.start_hand}} 初始</span>
...@@ -44,14 +44,14 @@ ...@@ -44,14 +44,14 @@
<span *ngIf="room.options.enable_priority != ygopro.default_options.enable_priority">旧规则</span> <span *ngIf="room.options.enable_priority != ygopro.default_options.enable_priority">旧规则</span>
<span *ngIf="room.options.no_check_deck != ygopro.default_options.no_check_deck">不检查</span> <span *ngIf="room.options.no_check_deck != ygopro.default_options.no_check_deck">不检查</span>
<span *ngIf="room.options.no_shuffle_deck != ygopro.default_options.no_shuffle_deck">不洗卡</span> <span *ngIf="room.options.no_shuffle_deck != ygopro.default_options.no_shuffle_deck">不洗卡</span>
</mat-cell> </md-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<mat-row *matRowDef="let room; columns: displayedColumns;" (click)="ygopro.join_room(room)"></mat-row> <md-row *cdkRowDef="let room; columns: displayedColumns;" (click)="ygopro.join_room(room)"></md-row>
</mat-table> </md-table>
<form (submit)="ygopro.join_private(joinPassword.value)" ngNativeValidate> <form (submit)="ygopro.join_private(joinPassword.value)" ngNativeValidate>
<input #joinPassword placeholder="在这输入你朋友的私密房间密码就可以进去了哦!" required> <input #joinPassword placeholder="在这输入你朋友的私密房间密码就可以进去了哦!" required>
<button mat-raised-button color="primary">加入私密房间</button> <button md-raised-button color="primary">加入私密房间</button>
</form> </form>
...@@ -120,7 +120,7 @@ export class StorageService { ...@@ -120,7 +120,7 @@ export class StorageService {
this.working = true; this.working = true;
// console.log('download', local_path, remote_path, index_path, time); // console.log('download', local_path, remote_path, index_path, time);
const data: Uint8Array = await this.client.getFileContents(remote_path); const data: Uint8Array = await this.client.getFileContents(remote_path);
window.ygopro.writeFile(local_path, Buffer.from(<ArrayBuffer>data.buffer).toString('base64')); window.ygopro.writeFile(local_path, Buffer.from(data.buffer).toString('base64'));
window.ygopro.setFileLastModified(local_path, time); window.ygopro.setFileLastModified(local_path, time);
// console.log(local_path, time); // console.log(local_path, time);
localStorage.setItem(index_path, time.toString()); localStorage.setItem(index_path, time.toString());
......
<mat-toolbar color="primary"> <md-toolbar color="primary">
<button mat-icon-button (click)="history.back()"> <button md-icon-button (click)="history.back()">
<mat-icon>&#xe5c4;</mat-icon><!--arrow_back--></button> <md-icon>&#xe5c4;</md-icon><!--arrow_back--></button>
<span><ng-content></ng-content></span> <span><ng-content></ng-content></span>
</mat-toolbar> </md-toolbar>
<app-toolbar>观战</app-toolbar> <app-toolbar>观战</app-toolbar>
<div class="hint" *ngIf="dataSource.loading"><mat-spinner></mat-spinner></div> <div class="hint" *ngIf="dataSource.loading"><md-spinner></md-spinner></div>
<div class="hint" *ngIf="dataSource.empty">现在没有进行中的游戏</div> <div class="hint" *ngIf="dataSource.empty">现在没有进行中的游戏</div>
<div class="hint" *ngIf="dataSource.error">网络错误</div> <div class="hint" *ngIf="dataSource.error">网络错误</div>
<mat-table #table [dataSource]="dataSource"> <md-table #table [dataSource]="dataSource">
<!--- Note that these columns can be defined in any order. <!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on room row definition" --> The actual rendered columns are set as a property on room row definition" -->
<!-- ID Column --> <!-- ID Column -->
<ng-container matColumnDef="mode"> <ng-container cdkColumnDef="mode">
<mat-header-cell *matHeaderCellDef>游戏模式</mat-header-cell> <md-header-cell *cdkHeaderCellDef>游戏模式</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<span i18n *ngIf="room.id.startsWith('AI#')">单人模式</span> <span i18n *ngIf="room.id.startsWith('AI#')">单人模式</span>
<span i18n *ngIf="room.arena === 'athletic'">竞技匹配</span> <span i18n *ngIf="room.arena === 'athletic'">竞技匹配</span>
<span i18n *ngIf="room.arena === 'entertain'">娱乐匹配</span> <span i18n *ngIf="room.arena === 'entertain'">娱乐匹配</span>
<span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 0">单局模式</span> <span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 0">单局模式</span>
<span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 1">比赛模式</span> <span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 1">比赛模式</span>
<span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 2">TAG</span> <span i18n *ngIf="!(room.arena || room.id.startsWith('AI#')) && room.options.mode === 2">TAG</span>
</mat-cell> </md-cell>
</ng-container> </ng-container>
<!-- ID Column --> <!-- ID Column -->
<ng-container matColumnDef="title"> <ng-container cdkColumnDef="title">
<mat-header-cell class="game-title" *matHeaderCellDef>游戏标题</mat-header-cell> <md-header-cell class="game-title" *cdkHeaderCellDef>游戏标题</md-header-cell>
<mat-cell class="game-title" *matCellDef="let room"> <md-cell class="game-title" *cdkCellDef="let room">
<span *ngIf="room.private">{{room.users[0] && room.users[0].username}}的私密房间</span> <span *ngIf="room.private">{{room.users[0] && room.users[0].username}}的私密房间</span>
<span i18n *ngIf="room.arena || room.id.startsWith('AI#')">{{room.users[0] && room.users[0].username}} Vs. {{room.users[1] && room.users[1].username}}</span> <span i18n *ngIf="room.arena || room.id.startsWith('AI#')">{{room.users[0] && room.users[0].username}} Vs. {{room.users[1] && room.users[1].username}}</span>
<span *ngIf="!(room.arena || room.id.startsWith('AI#') || room.private)">{{room.title}}</span> <span *ngIf="!(room.arena || room.id.startsWith('AI#') || room.private)">{{room.title}}</span>
</mat-cell> </md-cell>
</ng-container> </ng-container>
<!-- Progress Column --> <!-- Progress Column -->
<ng-container matColumnDef="users"> <ng-container cdkColumnDef="users">
<mat-header-cell *matHeaderCellDef>玩家</mat-header-cell> <md-header-cell *cdkHeaderCellDef>玩家</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<img *ngFor="let user of room.users" class="avatar" [src]="login.avatar(user.username)"> <img *ngFor="let user of room.users" class="avatar" [src]="login.avatar(user.username)">
</mat-cell> </md-cell>
</ng-container> </ng-container>
<!-- Color Column --> <!-- Color Column -->
<ng-container matColumnDef="extra"> <ng-container cdkColumnDef="extra">
<mat-header-cell *matHeaderCellDef>额外选项</mat-header-cell> <md-header-cell *cdkHeaderCellDef>额外选项</md-header-cell>
<mat-cell *matCellDef="let room"> <md-cell *cdkCellDef="let room">
<span *ngIf="room.options.rule != ygopro.default_options.rule">{{{'0': 'OCG', '1': 'TCG', '2': 'O/T', '3': '专有卡禁止'}[room.options.rule]}}</span> <span *ngIf="room.options.rule != ygopro.default_options.rule">{{{'0': 'OCG', '1': 'TCG', '2': 'O/T', '3': '专有卡禁止'}[room.options.rule]}}</span>
<span *ngIf="room.options.start_lp != ygopro.default_options.start_lp">{{room.options.start_lp}} LP</span> <span *ngIf="room.options.start_lp != ygopro.default_options.start_lp">{{room.options.start_lp}} LP</span>
<span *ngIf="room.options.start_hand != ygopro.default_options.start_hand">{{room.options.start_hand}} 初始</span> <span *ngIf="room.options.start_hand != ygopro.default_options.start_hand">{{room.options.start_hand}} 初始</span>
...@@ -51,10 +51,10 @@ ...@@ -51,10 +51,10 @@
<span *ngIf="room.options.enable_priority != ygopro.default_options.enable_priority">旧规则</span> <span *ngIf="room.options.enable_priority != ygopro.default_options.enable_priority">旧规则</span>
<span *ngIf="room.options.no_check_deck != ygopro.default_options.no_check_deck">不检查</span> <span *ngIf="room.options.no_check_deck != ygopro.default_options.no_check_deck">不检查</span>
<span *ngIf="room.options.no_shuffle_deck != ygopro.default_options.no_shuffle_deck">不洗卡</span> <span *ngIf="room.options.no_shuffle_deck != ygopro.default_options.no_shuffle_deck">不洗卡</span>
</mat-cell> </md-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
<mat-row *matRowDef="let room; columns: displayedColumns;" (click)="ygopro.join_room(room)"></mat-row> <md-row *cdkRowDef="let room; columns: displayedColumns;" (click)="ygopro.join_room(room)"></md-row>
</mat-table> </md-table>
<app-toolbar>单人模式</app-toolbar> <app-toolbar>单人模式</app-toolbar>
<mat-list> <md-list>
<h3 mat-subheader>选择对手</h3> <h3 md-subheader>选择对手</h3>
<mat-list-item (click)="ygopro.join_windbot()"> <md-list-item (click)="ygopro.join_windbot()">
<mat-icon mat-list-icon>&#xe195;</mat-icon><!-- airplanemode_active --> <md-icon md-list-icon>&#xe195;</md-icon><!-- airplanemode_active -->
<h4 mat-line>随机</h4> <h4 md-line>随机</h4>
</mat-list-item> </md-list-item>
<mat-list-item *ngFor="let windbot of ygopro.windbot" (click)="ygopro.join_windbot(windbot)"> <md-list-item *ngFor="let windbot of ygopro.windbot" (click)="ygopro.join_windbot(windbot)">
<img mat-list-icon [src]="login.avatar(windbot)"> <img md-list-icon [src]="login.avatar(windbot)">
<h4 mat-line>{{windbot}}</h4> <h4 md-line>{{windbot}}</h4>
</mat-list-item> </md-list-item>
</mat-list> </md-list>
import { DataSource } from '@angular/cdk';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { MdDialog } from '@angular/material';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { LoginService } from './login.service'; import { LoginService } from './login.service';
import { MatchDialogComponent } from './match/match.component'; import { MatchDialogComponent } from './match/match.component';
import { ResultDialogComponent } from './result/result.dialog'; import { ResultDialogComponent } from './result/result.dialog';
import { StorageService } from './storage.service'; import { StorageService } from './storage.service';
import { MatDialog } from '@angular/material';
import { DataSource } from '@angular/cdk/collections';
export interface User { export interface User {
admin: boolean; admin: boolean;
...@@ -23,10 +23,10 @@ export interface Room { ...@@ -23,10 +23,10 @@ export interface Room {
id?: string; id?: string;
title?: string; title?: string;
server?: Server; server?: Server;
private?: boolean; 'private'?: boolean;
options: Options; options: Options;
arena?: string; arena?: string;
users?: { username: string; position: number }[]; users?: { username: string, position: number }[];
} }
export interface Options { export interface Options {
...@@ -42,6 +42,7 @@ export interface Options { ...@@ -42,6 +42,7 @@ export interface Options {
time_limit?: number; time_limit?: number;
} }
export interface Server { export interface Server {
id?: string; id?: string;
url?: string; url?: string;
...@@ -109,6 +110,7 @@ export interface Points { ...@@ -109,6 +110,7 @@ export interface Points {
@Injectable() @Injectable()
export class YGOProService { export class YGOProService {
news: News[]; news: News[];
windbot: string[]; windbot: string[];
topics: Observable<any>; topics: Observable<any>;
...@@ -129,62 +131,52 @@ export class YGOProService { ...@@ -129,62 +131,52 @@ export class YGOProService {
time_limit: 180 time_limit: 180
}; };
readonly servers: Server[] = [ readonly servers: Server[] = [{
{
id: 'tiramisu', id: 'tiramisu',
url: 'wss://tiramisu.mycard.moe:7923', url: 'wss://tiramisu.mycard.moe:7923',
address: '112.124.105.11', address: '112.124.105.11',
port: 7911, port: 7911,
custom: true, custom: true,
replay: true replay: true
}, }, {
{
id: 'tiramisu-athletic', id: 'tiramisu-athletic',
url: 'wss://tiramisu.mycard.moe:8923', url: 'wss://tiramisu.mycard.moe:8923',
address: '112.124.105.11', address: '112.124.105.11',
port: 8911, port: 8911,
custom: false, custom: false,
replay: true replay: true
} }];
];
constructor(private login: LoginService, private http: Http, private dialog: MatDialog, private storage: StorageService) { constructor(private login: LoginService, private http: Http, private dialog: MdDialog, private storage: StorageService) {
this.load().catch(alert); this.load().catch(alert);
} }
async load() { async load() {
const apps: App[] = await this.http
.get('https://api.mycard.moe/apps.json') const apps: App[] = await this.http.get('https://api.mycard.moe/apps.json').map(response => response.json()).toPromise();
.map(response => response.json())
.toPromise();
const app = apps.find(_app => _app.id === 'ygopro')!; const app = apps.find(_app => _app.id === 'ygopro')!;
this.news = app.news['zh-CN']; this.news = app.news['zh-CN'];
this.windbot = (<YGOProData>app.data).windbot['zh-CN']; this.windbot = (<YGOProData>app.data).windbot['zh-CN'];
this.topics = this.http.get('https://ygobbs.com/top/quarterly.json').map(response => this.topics = this.http.get('https://ygobbs.com/top/quarterly.json').map(
response response => response.json().topic_list.topics.slice(0, 5).map((topic: any) => ({
.json()
.topic_list.topics.slice(0, 5)
.map((topic: any) => ({
...topic, ...topic,
url: new URL(`/t/${topic.slug}/${topic.id}`, 'https://ygobbs.com').toString(), 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() image_url: topic.image_url && new URL(topic.image_url, 'https://ygobbs.com').toString()
})) })));
);
this.storage.sync('ygopro'); this.storage.sync('ygopro');
this.load_points(); this.load_points();
await this.load_result(false); await this.load_result(false);
this.listen_result(); this.listen_result();
} }
async load_result(load_points = true) { async load_result(load_points = true) {
const last = await this.http
.get('https://mycard.moe/ygopro/api/history', { const last = await this.http.get('https://mycard.moe/ygopro/api/history', {
params: { username: this.login.user.username, type: 0, page_num: 1 } params: {username: this.login.user.username, type: 0, page_num: 1}
}) }).map((response) => response.json().data[0]).toPromise();
.map(response => response.json().data[0])
.toPromise();
// 从来没打过 // 从来没打过
if (!last) { if (!last) {
...@@ -209,10 +201,7 @@ export class YGOProService { ...@@ -209,10 +201,7 @@ export class YGOProService {
if (load_points) { if (load_points) {
this.load_points(); this.load_points();
} }
const again = await this.dialog const again = await this.dialog.open(ResultDialogComponent, {data: last}).afterClosed().toPromise();
.open(ResultDialogComponent, { data: last })
.afterClosed()
.toPromise();
if (again) { if (again) {
this.request_match(last.type); this.request_match(last.type);
} }
...@@ -220,20 +209,18 @@ export class YGOProService { ...@@ -220,20 +209,18 @@ export class YGOProService {
} }
async request_match(arena: string) { async request_match(arena: string) {
const data = await this.dialog const data = await this.dialog.open(MatchDialogComponent, {data: arena, disableClose: true}).afterClosed().toPromise();
.open(MatchDialogComponent, { data: arena, disableClose: true })
.afterClosed()
.toPromise();
if (data) { if (data) {
this.join(data['password'], { address: data['address'], port: data['port'] }); this.join(data['password'], {address: data['address'], port: data['port']});
} }
} }
listen_result() { listen_result() {
// 那些兼容性的垃圾事儿 // 那些兼容性的垃圾事儿
// https://www.html5rocks.com/en/tutorials/pagevisibility/intro/ // https://www.html5rocks.com/en/tutorials/pagevisibility/intro/
const hidden = ['hidden', 'webkitHidden', 'mozHidden', 'msHidden', 'oHidden'].find(prop => prop in document); const hidden = ['hidden', 'webkitHidden', 'mozHidden', 'msHidden', 'oHidden'].find((prop) => prop in document);
if (hidden) { if (hidden) {
const evtname = hidden.replace(/[H|h]idden/, '') + 'visibilitychange'; const evtname = hidden.replace(/[H|h]idden/, '') + 'visibilitychange';
Observable.fromEvent(document, evtname).subscribe(() => { Observable.fromEvent(document, evtname).subscribe(() => {
...@@ -251,10 +238,8 @@ export class YGOProService { ...@@ -251,10 +238,8 @@ export class YGOProService {
} }
async load_points() { async load_points() {
this.points = await this.http this.points = await this.http.get('https://api.mycard.moe/ygopro/arena/user', {params: {username: this.login.user.username}}).map(
.get('https://api.mycard.moe/ygopro/arena/user', { params: { username: this.login.user.username } }) response => response.json()).toPromise();
.map(response => response.json())
.toPromise();
} }
create_room(room: Room, host_password: string) { create_room(room: Room, host_password: string) {
...@@ -262,28 +247,27 @@ export class YGOProService { ...@@ -262,28 +247,27 @@ export class YGOProService {
// 建主密码 https://docs.google.com/document/d/1rvrCGIONua2KeRaYNjKBLqyG9uybs9ZI-AmzZKNftOI/edit // 建主密码 https://docs.google.com/document/d/1rvrCGIONua2KeRaYNjKBLqyG9uybs9ZI-AmzZKNftOI/edit
options_buffer.writeUInt8((room.private ? 2 : 1) << 4, 1); options_buffer.writeUInt8((room.private ? 2 : 1) << 4, 1);
options_buffer.writeUInt8( options_buffer.writeUInt8(
(room.options.rule << 5) | room.options.rule << 5 |
(room.options.mode << 3) | room.options.mode << 3 |
(room.options.enable_priority ? 1 << 2 : 0) | (room.options.enable_priority ? 1 << 2 : 0) |
(room.options.no_check_deck ? 1 << 1 : 0) | (room.options.no_check_deck ? 1 << 1 : 0) |
(room.options.no_shuffle_deck ? 1 : 0), (room.options.no_shuffle_deck ? 1 : 0)
2 , 2);
);
options_buffer.writeUInt16LE(room.options.start_lp, 3); options_buffer.writeUInt16LE(room.options.start_lp, 3);
options_buffer.writeUInt8((room.options.start_hand << 4) | room.options.draw_count, 5); options_buffer.writeUInt8(room.options.start_hand << 4 | room.options.draw_count, 5);
let checksum = 0; let checksum = 0;
for (let i = 1; i < options_buffer.length; i++) { for (let i = 1; i < options_buffer.length; i++) {
checksum -= options_buffer.readUInt8(i); checksum -= options_buffer.readUInt8(i);
} }
options_buffer.writeUInt8(checksum & 0xff, 0); options_buffer.writeUInt8(checksum & 0xFF, 0);
const secret = this.login.user.external_id % 65535 + 1; const secret = this.login.user.external_id % 65535 + 1;
for (let i = 0; i < options_buffer.length; i += 2) { for (let i = 0; i < options_buffer.length; i += 2) {
options_buffer.writeUInt16LE(options_buffer.readUInt16LE(i) ^ secret, i); options_buffer.writeUInt16LE(options_buffer.readUInt16LE(i) ^ secret, i);
} }
const password = const password = options_buffer.toString('base64') + (room.private ? host_password :
options_buffer.toString('base64') + (room.private ? host_password : room.title!.replace(/\s/, String.fromCharCode(0xfeff))); room.title!.replace(/\s/, String.fromCharCode(0xFEFF)));
// let room_id = crypto.createHash('md5').update(password + this.loginService.user.username).digest('base64') // let room_id = crypto.createHash('md5').update(password + this.loginService.user.username).digest('base64')
// .slice(0, 10).replace('+', '-').replace('/', '_'); // .slice(0, 10).replace('+', '-').replace('/', '_');
...@@ -302,7 +286,7 @@ export class YGOProService { ...@@ -302,7 +286,7 @@ export class YGOProService {
for (let i = 1; i < options_buffer.length; i++) { for (let i = 1; i < options_buffer.length; i++) {
checksum -= options_buffer.readUInt8(i); checksum -= options_buffer.readUInt8(i);
} }
options_buffer.writeUInt8(checksum & 0xff, 0); options_buffer.writeUInt8(checksum & 0xFF, 0);
const secret = this.login.user.external_id % 65535 + 1; const secret = this.login.user.external_id % 65535 + 1;
for (let i = 0; i < options_buffer.length; i += 2) { for (let i = 0; i < options_buffer.length; i += 2) {
...@@ -321,14 +305,14 @@ export class YGOProService { ...@@ -321,14 +305,14 @@ export class YGOProService {
for (let i = 1; i < options_buffer.length; i++) { for (let i = 1; i < options_buffer.length; i++) {
checksum -= options_buffer.readUInt8(i); checksum -= options_buffer.readUInt8(i);
} }
options_buffer.writeUInt8(checksum & 0xff, 0); options_buffer.writeUInt8(checksum & 0xFF, 0);
const secret = this.login.user.external_id % 65535 + 1; const secret = this.login.user.external_id % 65535 + 1;
for (let i = 0; i < options_buffer.length; i += 2) { for (let i = 0; i < options_buffer.length; i += 2) {
options_buffer.writeUInt16LE(options_buffer.readUInt16LE(i) ^ secret, i); options_buffer.writeUInt16LE(options_buffer.readUInt16LE(i) ^ secret, i);
} }
const name = options_buffer.toString('base64') + password.replace(/\s/, String.fromCharCode(0xfeff)); const name = options_buffer.toString('base64') + password.replace(/\s/, String.fromCharCode(0xFEFF));
this.join(name, this.servers[0]); this.join(name, this.servers[0]);
} }
...@@ -345,12 +329,10 @@ export class YGOProService { ...@@ -345,12 +329,10 @@ export class YGOProService {
window.ygopro.join(server.address, server.port, this.login.user.username, password); window.ygopro.join(server.address, server.port, this.login.user.username, password);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert( alert(JSON.stringify({
JSON.stringify({
method: 'join', method: 'join',
params: [server.address, server.port, this.login.user.username, password] params: [server.address, server.port, this.login.user.username, password]
}) }));
);
} }
} }
...@@ -359,7 +341,7 @@ export class YGOProService { ...@@ -359,7 +341,7 @@ export class YGOProService {
window.ygopro.edit_deck(); window.ygopro.edit_deck();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'edit_deck', params: [] })); alert(JSON.stringify({method: 'edit_deck', params: []}));
} }
} }
...@@ -368,7 +350,7 @@ export class YGOProService { ...@@ -368,7 +350,7 @@ export class YGOProService {
window.ygopro.watch_replay(); window.ygopro.watch_replay();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'watch_replay', params: [] })); alert(JSON.stringify({method: 'watch_replay', params: []}));
} }
} }
...@@ -377,7 +359,7 @@ export class YGOProService { ...@@ -377,7 +359,7 @@ export class YGOProService {
window.ygopro.puzzle_mode(); window.ygopro.puzzle_mode();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'puzzle_mode', params: [] })); alert(JSON.stringify({method: 'puzzle_mode', params: []}));
} }
} }
...@@ -386,7 +368,7 @@ export class YGOProService { ...@@ -386,7 +368,7 @@ export class YGOProService {
window.ygopro.openDrawer(); window.ygopro.openDrawer();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'openDrawer', params: [] })); alert(JSON.stringify({method: 'openDrawer', params: []}));
} }
} }
...@@ -395,7 +377,7 @@ export class YGOProService { ...@@ -395,7 +377,7 @@ export class YGOProService {
window.ygopro.backHome(); window.ygopro.backHome();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'backHome', params: [] })); alert(JSON.stringify({method: 'backHome', params: []}));
} }
} }
...@@ -404,18 +386,21 @@ export class YGOProService { ...@@ -404,18 +386,21 @@ export class YGOProService {
window.ygopro.share(text); window.ygopro.share(text);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
alert(JSON.stringify({ method: 'share', params: [text] })); alert(JSON.stringify({method: 'share', params: [text]}));
} }
} }
} }
type Message = type Message =
| { event: 'init'; data: Room[] } { event: 'init', data: Room[] }
| { event: 'update'; data: Room } | { event: 'update', data: Room }
| { event: 'create'; data: Room } | { event: 'create', data: Room }
| { event: 'delete'; data: string }; | { event: 'delete', data: string };
export class RoomListDataSource extends DataSource<any> {
export class RoomListDataSource extends DataSource<Room> {
loading = true; loading = true;
empty = false; empty = false;
error: any; error: any;
...@@ -426,18 +411,18 @@ export class RoomListDataSource extends DataSource<Room> { ...@@ -426,18 +411,18 @@ export class RoomListDataSource extends DataSource<Room> {
/** Connect function called by the table to retrieve one stream containing the data to render. */ /** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<Room[]> { connect(): Observable<Room[]> {
return (
Observable.combineLatest( return Observable.combineLatest(this.servers.map(server => {
this.servers.map(server => {
const url = new URL(server.url!); const url = new URL(server.url!);
url.searchParams.set('filter', this.filter); url.searchParams.set('filter', this.filter);
// 协议处理 // 协议处理
return Observable.webSocket<Message>(url.toString()).scan((rooms: Room[], message: Message, index: number) => { return Observable.webSocket({url: url.toString()})
.scan((rooms: Room[], message: Message) => {
switch (message.event) { switch (message.event) {
case 'init': case 'init':
return message.data.map(room => ({ server: server, ...room })); return message.data.map(room => ({server: server, ...room}));
case 'create': case 'create':
return rooms.concat({ server: server, ...message.data }); return rooms.concat({server: server, ...message.data});
case 'update': case 'update':
Object.assign(rooms.find(room => room.id === message.data.id), message.data); Object.assign(rooms.find(room => room.id === message.data.id), message.data);
return rooms; return rooms;
...@@ -446,13 +431,9 @@ export class RoomListDataSource extends DataSource<Room> { ...@@ -446,13 +431,9 @@ export class RoomListDataSource extends DataSource<Room> {
} }
}, []); }, []);
// 把多个服务器的数据拼接起来,这里是 combineLatest 的第二个参数 // 把多个服务器的数据拼接起来,这里是 combineLatest 的第二个参数
}), }), (...sources: Room[][]) => (<Room[]>[]).concat(...sources))
(...sources: Room[][]) => (<Room[]>[]).concat(...sources)
)
// 房间排序 // 房间排序
.map( .map(rooms => sortBy(rooms, (room) => {
rooms =>
sortBy(rooms, room => {
if (room.arena === 'athletic') { if (room.arena === 'athletic') {
return 0; return 0;
} else if (room.arena === 'entertain') { } else if (room.arena === 'entertain') {
...@@ -464,19 +445,18 @@ export class RoomListDataSource extends DataSource<Room> { ...@@ -464,19 +445,18 @@ export class RoomListDataSource extends DataSource<Room> {
} }
}) })
// loading、empty、error // loading、empty、error
) ).filter((rooms) => {
.filter(rooms => {
this.loading = false; this.loading = false;
this.empty = rooms.length === 0; this.empty = rooms.length === 0;
return true; return true;
}) }).catch((error) => {
.catch(error => {
this.loading = false; this.loading = false;
this.error = error; this.error = error;
return []; return [];
}) });
); }
disconnect() {
} }
disconnect() {}
} }
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
"typeRoots": [ "typeRoots": [
"node_modules/@types" "node_modules/@types"
], ],
"skipLibCheck": true,
"lib": [ "lib": [
"esnext", "esnext",
"dom.iterable" "dom.iterable"
......
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