Commit 179df0f7 authored by 神楽坂玲奈's avatar 神楽坂玲奈

将 Candy 作为 Component 而不是 WebView

tslint
parent 648cf20c
<p>时隔多年,终于又跟</p> <!--<h2>MyCard 招募公告</h2>-->
<h2>招人</h2> <!--<p>MyCard 伴随大家已经有 6 年了,在这 6 年间 MyCard 作为一个同人平台很感谢得到大家的支持,现在 MyCard 为了给支持的大家带来更好的体验,正在努力进行全新的改版的开发工作,希望可以得到大家的支持和帮助。</p>--><!--<p>职位:(前端)开发工程师</p>--><!--<p>负责平台客户端的开发,及网站和论坛相关的改版工作。</p>--><!--<p>职位描述:对 ACG 领域有一定的了解,会js等编程领域的专业技能,对软件开发具有一定的热情和自主能动性,认真严谨和团队意识。</p>--><!--<p>联系邮箱:hr@mycard.moe</p>--><!--<p>工作地点:上海市长宁区(工资面议)</p>-->
<h3>前端工程师</h3>
<p>热爱,圈内优先</p>
<p>HTML5, CSS, JavaScript</p>
<p>至少会一个前端框架 Angular React Vue</p>
<p>坐标上海,全职,不远程,工资面议</p>
<h3>后端工程师</h3> <!--<h2>联系我们</h2>--><!--<dl>--><!--<dt>应聘</dt>--><!--<dd>hr@mycard.com</dd>--><!--<dt>投稿、合作、侵权投诉</dt>--><!--<dd>business@mycard.com</dd>--><!--<dt>问题反馈</dt>--><!--<dd>support@mycard.moe</dd>--><!--</dl>-->
<p>热爱,圈内优先</p>
<p>会 php、, CSS, JavaScript</p>
<p>至少会一个前端框架 Angular React Vue</p>
<p>坐标上海,全职,不远程,工资面议</p>
<h2>联系我们</h2>
<dl>
<dt>游戏投稿、合作、应聘、侵权投诉</dt>
<dd>business@mycard.com</dd>
<dt>问题反馈</dt>
<dd>support@mycard.moe</dd>
</dl>
\ No newline at end of file
/** /**
* Created by zh99998 on 16/9/2. * Created by zh99998 on 16/9/2.
*/ */
import {Component} from "@angular/core"; import {Component} from '@angular/core';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
selector: 'about', selector: 'about',
......
import {Component, OnInit, Input, ChangeDetectorRef} from "@angular/core"; import {Component, OnInit, Input, ChangeDetectorRef} from '@angular/core';
import {AppsService} from "./apps.service"; import {AppsService} from './apps.service';
import {InstallOption} from "./install-option"; import {InstallOption} from './install-option';
import {SettingsService} from "./settings.sevices"; import {SettingsService} from './settings.sevices';
import {App} from "./app"; import {App} from './app';
import {DownloadService} from "./download.service"; import {DownloadService} from './download.service';
import {clipboard, remote} from "electron"; import {clipboard, remote} from 'electron';
import * as path from "path"; import * as path from 'path';
import * as fs from "fs"; import * as fs from 'fs';
import * as $ from 'jquery';
declare const Notification: any; declare const Notification: any;
declare const $: any;
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
...@@ -37,17 +37,17 @@ export class AppDetailComponent implements OnInit { ...@@ -37,17 +37,17 @@ export class AppDetailComponent implements OnInit {
let volume = 'A'; let volume = 'A';
for (let i = 0; i < 26; i++) { for (let i = 0; i < 26; i++) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
let currentVolume = String.fromCharCode(volume.charCodeAt(0) + i) + ":"; let currentVolume = String.fromCharCode(volume.charCodeAt(0) + i) + ':';
fs.access(currentVolume, (err) => { fs.access(currentVolume, (err) => {
if (!err) { if (!err) {
//判断是否已经存在Library // 判断是否已经存在Library
if (this.libraries.every((library) => !library.startsWith(currentVolume))) { if (this.libraries.every((library) => !library.startsWith(currentVolume))) {
this.availableLibraries.push(currentVolume); this.availableLibraries.push(currentVolume);
} }
} }
resolve() resolve();
}) });
}) });
} }
} }
...@@ -61,7 +61,8 @@ export class AppDetailComponent implements OnInit { ...@@ -61,7 +61,8 @@ export class AppDetailComponent implements OnInit {
if (reference.isLanguage()) { if (reference.isLanguage()) {
// 对于语言包,只有在语言包的locales比游戏本身的更加合适的时候才默认勾选 // 对于语言包,只有在语言包的locales比游戏本身的更加合适的时候才默认勾选
// 这里先偷个懒,中文环境勾选中文语言包,非中文环境勾选非中文语言包 // 这里先偷个懒,中文环境勾选中文语言包,非中文环境勾选非中文语言包
this.referencesInstall[reference.id] = reference.locales[0].startsWith('zh') == this.settingsService.getLocale().startsWith('zh') this.referencesInstall[reference.id] =
reference.locales[0].startsWith('zh') === this.settingsService.getLocale().startsWith('zh');
} else { } else {
this.referencesInstall[reference.id] = true; this.referencesInstall[reference.id] = true;
} }
...@@ -88,7 +89,7 @@ export class AppDetailComponent implements OnInit { ...@@ -88,7 +89,7 @@ export class AppDetailComponent implements OnInit {
} }
async uninstall(app: App) { async uninstall(app: App) {
if (confirm("确认删除?")) { if (confirm('确认删除?')) {
try { try {
await this.appsService.uninstall(app); await this.appsService.uninstall(app);
} catch (e) { } catch (e) {
...@@ -105,35 +106,35 @@ export class AppDetailComponent implements OnInit { ...@@ -105,35 +106,35 @@ export class AppDetailComponent implements OnInit {
for (let [id, install] of Object.entries(referencesInstall)) { for (let [id, install] of Object.entries(referencesInstall)) {
if (install) { if (install) {
let reference = targetApp.references.get(id)!; let reference = targetApp.references.get(id)!;
console.log("reference install ", id, targetApp, targetApp.references, reference); console.log('reference install ', id, targetApp, targetApp.references, reference);
await this.appsService.install(reference, options); await this.appsService.install(reference, options);
} }
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
new Notification(targetApp.name, {body: "下载失败"}); new Notification(targetApp.name, {body: '下载失败'});
} }
} }
async selectLibrary() { async selectLibrary() {
if (this.installOption.installLibrary.startsWith('create_')) { if (this.installOption.installLibrary.startsWith('create_')) {
let volume = this.installOption.installLibrary.slice(7); let volume = this.installOption.installLibrary.slice(7);
let library = path.join(volume, "MyCardLibrary"); let library = path.join(volume, 'MyCardLibrary');
try { try {
await this.appsService.createDirectory(library); await this.appsService.createDirectory(library);
this.installOption.installLibrary = library; this.installOption.installLibrary = library;
this.settingsService.addLibrary(library, true); this.settingsService.addLibrary(library, true);
} catch (e) { } catch (e) {
this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path; this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path;
alert("无法创建指定目录"); alert('无法创建指定目录');
} finally { } finally {
let index = this.availableLibraries.findIndex((l) => { let index = this.availableLibraries.findIndex((l) => {
return l === volume return l === volume;
}); });
this.availableLibraries.splice(index, 1); this.availableLibraries.splice(index, 1);
} }
} else { } else {
this.settingsService.setDefaultLibrary({path: this.installOption.installLibrary, "default": true}) this.settingsService.setDefaultLibrary({path: this.installOption.installLibrary, 'default': true});
} }
this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path; this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path;
} }
...@@ -162,13 +163,13 @@ export class AppDetailComponent implements OnInit { ...@@ -162,13 +163,13 @@ export class AppDetailComponent implements OnInit {
for (let [id, install] of Object.entries(referencesInstall)) { for (let [id, install] of Object.entries(referencesInstall)) {
if (install) { if (install) {
let reference = targetApp.references.get(id)!; let reference = targetApp.references.get(id)!;
console.log("reference install ", id, targetApp, targetApp.references, reference); console.log('reference install ', id, targetApp, targetApp.references, reference);
await this.appsService.install(reference, option); await this.appsService.install(reference, option);
} }
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
new Notification(targetApp.name, {body: "导入失败"}); new Notification(targetApp.name, {body: '导入失败'});
} }
} }
...@@ -182,7 +183,7 @@ export class AppDetailComponent implements OnInit { ...@@ -182,7 +183,7 @@ export class AppDetailComponent implements OnInit {
await this.appsService.update(mod, true); await this.appsService.update(mod, true);
} }
} catch (e) { } catch (e) {
new Notification(app.name, {body: "校验失败"}); new Notification(app.name, {body: '校验失败'});
console.error(e); console.error(e);
} }
} }
...@@ -194,10 +195,10 @@ export class AppDetailComponent implements OnInit { ...@@ -194,10 +195,10 @@ export class AppDetailComponent implements OnInit {
async selectImport(app: App) { async selectImport(app: App) {
let main = app.actions.get('main'); let main = app.actions.get('main');
if (!main) { if (!main) {
return return;
} }
if (!main.execute) { if (!main.execute) {
return return;
} }
let filename = main.execute.split('/')[0]; let filename = main.execute.split('/')[0];
let extname = path.extname(filename).slice(1); let extname = path.extname(filename).slice(1);
...@@ -206,12 +207,12 @@ export class AppDetailComponent implements OnInit { ...@@ -206,12 +207,12 @@ export class AppDetailComponent implements OnInit {
let filePaths = await new Promise((resolve, reject) => { let filePaths = await new Promise((resolve, reject) => {
remote.dialog.showOpenDialog({ remote.dialog.showOpenDialog({
filters: [{name: filename, extensions: [extname]}], filters: [{name: filename, extensions: [extname]}],
properties: ['openFile',] properties: ['openFile']
}, resolve) }, resolve);
}); });
if (filePaths && filePaths[0]) { if (filePaths && filePaths[0]) {
this.import_path = filePaths[0] this.import_path = filePaths[0];
} }
} }
......
import {App} from "./app"; import {App} from './app';
/** /**
* Created by zh99998 on 16/9/6. * Created by zh99998 on 16/9/6.
*/ */
export class AppLocal { export class AppLocal {
path: string; path: string;
version: string; version: string;
files: Map<string,string>; files: Map<string, string>;
action: Map<string,{execute: string, args: string[], env: {}, open: App}>; action: Map<string, {execute: string, args: string[], env: {}, open: App}>;
update(local: any) { update(local: any) {
this.path = local.path; this.path = local.path;
this.version = local.version; this.version = local.version;
let files = new Map<string,string>(); let files = new Map<string, string>();
for (let filename of Object.keys(local.files)) { for (let filename of Object.keys(local.files)) {
files.set(filename, local.files[filename]); files.set(filename, local.files[filename]);
} }
...@@ -20,7 +20,7 @@ export class AppLocal { ...@@ -20,7 +20,7 @@ export class AppLocal {
toJSON() { toJSON() {
let t: any = {}; let t: any = {};
for (let [k,v] of this.files) { for (let [k, v] of this.files) {
t[k] = v; t[k] = v;
} }
return {path: this.path, version: this.version, files: t}; return {path: this.path, version: this.version, files: t};
......
import {AppLocal} from "./app-local"; import {AppLocal} from './app-local';
export enum Category { export enum Category {
game, game,
...@@ -24,7 +24,7 @@ export interface Action { ...@@ -24,7 +24,7 @@ export interface Action {
execute: string; execute: string;
args: string[]; args: string[];
env: {}; env: {};
open?: App open?: App;
} }
export class FileOptions { export class FileOptions {
sync: boolean; sync: boolean;
...@@ -36,7 +36,7 @@ export class AppStatus { ...@@ -36,7 +36,7 @@ export class AppStatus {
total: number; total: number;
private _status: string; private _status: string;
get status(): string { get status(): string {
return this._status return this._status;
} }
set status(status: string) { set status(status: string) {
...@@ -52,56 +52,56 @@ export class App { ...@@ -52,56 +52,56 @@ export class App {
id: string; id: string;
name: string; // i18n name: string; // i18n
description: string; //i18n description: string; // i18n
author: string; // English Only author: string; // English Only
homepage: string; homepage: string;
category: Category; category: Category;
parent?: App; parent?: App;
static downloadUrl(app: App, platform: string, locale: string): string { actions: Map<string, Action>;
if (app.id === "ygopro") { references: Map<string, App>;
return `https://thief.mycard.moe/metalinks/${app.id}-${process.platform}-${locale}/${app.version}` dependencies: Map<string, App>;
} else if (app.id === "desmume") { locales: string[];
return `https://thief.mycard.moe/metalinks/${app.id}-${process.platform}/${app.version}` news: {title: string, url: string, image: string}[];
network: any;
tags: string[];
version: string;
local: AppLocal | null;
status: AppStatus;
conference: string | undefined;
files: Map<string, FileOptions>;
data: any;
static downloadUrl (app: App, platform: string, locale: string): string {
if (app.id === 'ygopro') {
return `https://thief.mycard.moe/metalinks/${app.id}-${process.platform}-${locale}/${app.version}`;
} else if (app.id === 'desmume') {
return `https://thief.mycard.moe/metalinks/${app.id}-${process.platform}/${app.version}`;
} }
return `https://thief.mycard.moe/metalinks/${app.id}/${app.version}`; return `https://thief.mycard.moe/metalinks/${app.id}/${app.version}`;
} }
static checksumUrl(app: App, platform: string, locale: string): string { static checksumUrl (app: App, platform: string, locale: string): string {
if (app.id === "ygopro") { if (app.id === 'ygopro') {
return `https://thief.mycard.moe/checksums/${app.id}-${platform}-${locale}/${app.version}` return `https://thief.mycard.moe/checksums/${app.id}-${platform}-${locale}/${app.version}`;
} else if (app.id === "desmume") { } else if (app.id === 'desmume') {
return `https://thief.mycard.moe/checksums/${app.id}-${platform}/${app.version}` return `https://thief.mycard.moe/checksums/${app.id}-${platform}/${app.version}`;
} }
return `https://thief.mycard.moe/checksums/${app.id}/${app.version}` return `https://thief.mycard.moe/checksums/${app.id}/${app.version}`;
} }
static updateUrl(app: App, platform: string, locale: string): string { static updateUrl (app: App, platform: string, locale: string): string {
if (app.id === "ygopro") { if (app.id === 'ygopro') {
return `https://thief.mycard.moe/update/${app.id}-${platform}-${locale}/${app.version}`; return `https://thief.mycard.moe/update/${app.id}-${platform}-${locale}/${app.version}`;
} else if (app.id === "desmume") { } else if (app.id === 'desmume') {
return `https://thief.mycard.moe/update/${app.id}-${platform}/${app.version}`; return `https://thief.mycard.moe/update/${app.id}-${platform}/${app.version}`;
} }
return `https://thief.mycard.moe/update/${app.id}/${app.version}`; return `https://thief.mycard.moe/update/${app.id}/${app.version}`;
} }
actions: Map<string,Action>;
references: Map<string,App>;
dependencies: Map<string,App>;
locales: string[];
news: {title: string, url: string, image: string}[];
network: any;
tags: string[];
version: string;
local: AppLocal | null;
status: AppStatus;
conference: string | undefined;
files: Map<string,FileOptions>;
data: any;
isLanguage() { isLanguage() {
return this.category == Category.module && this.tags.includes('language'); return this.category === Category.module && this.tags.includes('language');
} }
reset() { reset() {
...@@ -111,31 +111,31 @@ export class App { ...@@ -111,31 +111,31 @@ export class App {
} }
isInstalled(): boolean { isInstalled(): boolean {
return this.status.status != 'init'; return this.status.status !== 'init';
} }
isReady(): boolean { isReady(): boolean {
return this.status.status == 'ready'; return this.status.status === 'ready';
} }
isInstalling(): boolean { isInstalling(): boolean {
return this.status.status == 'installing'; return this.status.status === 'installing';
} }
isWaiting(): boolean { isWaiting(): boolean {
return this.status.status == 'waiting'; return this.status.status === 'waiting';
} }
isDownloading(): boolean { isDownloading(): boolean {
return this.status.status === "downloading"; return this.status.status === 'downloading';
} }
isUninstalling(): boolean { isUninstalling(): boolean {
return this.status.status === "uninstalling"; return this.status.status === 'uninstalling';
} }
isUpdating(): boolean { isUpdating(): boolean {
return this.status.status === "updating"; return this.status.status === 'updating';
} }
runnable(): boolean { runnable(): boolean {
...@@ -187,4 +187,4 @@ export class App { ...@@ -187,4 +187,4 @@ export class App {
return dependencies.every((dependency) => dependency.isReady()); return dependencies.every((dependency) => dependency.isReady());
} }
} }
\ No newline at end of file
This diff is collapsed.
/* Turn on custom 8px wide scrollbar */
::-webkit-scrollbar {
width: 8px; /* 1px wider than Lion. */
/* This is more usable for users trying to click it. */
background-color: rgba(0, 0, 0, 0);
-webkit-border-radius: 100px;
}
/* hover effect for both scrollbar area, and scrollbar 'thumb' */
::-webkit-scrollbar:active {
background-color: rgba(0, 0, 0, 0.05);
}
/* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */
::-webkit-scrollbar-thumb:vertical {
/* This is the EXACT color of Mac OS scrollbars.
Yes, I pulled out digital color meter */
background: rgba(0, 0, 0, 0.1);
-webkit-border-radius: 100px;
}
::-webkit-scrollbar-thumb:vertical:active {
background: rgba(0, 0, 0, 0.2); /* Some darker color when you click it */
-webkit-border-radius: 100px;
}
.scroll {
overflow-y: hidden;
}
.scroll:hover {
overflow-y: auto;
}
.message-pane-wrapper, .message-form-wrapper {
margin-right: 190px;
}
.roster-pane, #chat-toolbar {
width: 190px
}
#candy {
background-color: #f7f7f9;
}
#chat-tabs li {
box-shadow: 0 0 1px 1px #ccc;
}
#chat-tabs a {
color: #999;
background-color: #ececec;
}
#chat-tabs .active a.label {
background-color: white;
}
#chat-tabs .active .transition {
background: white none;
}
#chat-tabs .transition {
background: #ececec none;
}
.message-pane-wrapper {
background-color: white;
padding: 0;
}
.message-pane li {
border-bottom: none;
box-shadow: none;
padding: 0 5px;
}
.message-pane li > div {
padding: 0 0 0 150px;
line-height: 28px;
}
.message-pane .label {
margin-left: -150px;
width: 130px;
}
.roster-pane {
background-color: initial;
border-top: none;
box-shadow: none;
margin: 30px 0 32px 0;
}
.roster-pane .user {
border-bottom: none;
box-shadow: none;
color: black;
}
.roster-pane .user:hover {
background-color: #ebf3f8;
}
#chat-toolbar {
position: absolute;
background-color: initial;
border-top: 1px solid #eee;
box-shadow: none;
}
.message-form-wrapper {
position: absolute;
border-top: 1px solid #eee;
}
.message-form {
position: absolute;
margin-right: 20px;
}
.message-form input.submit {
position: absolute;
margin-right: 0
}
.roster-pane .label {
text-shadow: none
}
#candy {
border-top: 1px solid #eee;
box-shadow: inset 0 1px 2px white;
}
.usercount span {
background-color: initial;
color: #a7a7a7;
}
#chat-modal.modal-common {
position: absolute;
}
/*#context-menu {*/
/*margin-top: 0;*/
/*padding-top: 0;*/
/*}*/
#context-menu ul {
overflow-y: auto;
max-height: 170px;
}
/*#context-menu {*/
/**/
/*}*/
\ No newline at end of file
<div id="candy"></div>
\ No newline at end of file
/**
* Created by zh99998 on 16/9/2.
*/
let shadow: ShadowRoot;
const jQueryOriginal = window['jQuery'];
const jQueryShadow = require('../jquery-shadow.js');
jQueryShadow.fn.init = new Proxy(jQueryShadow.fn.init, {
construct(target, argumentsList, newTarget) {
let [selector, context, root] = argumentsList;
if (shadow) {
if (selector === 'body') {
selector = shadow;
} else if (selector === document) {
selector = shadow.querySelector('#candy');
} else if (!context) {
context = shadow;
}
}
return new target(selector, context, root);
}
});
window['jQuery'] = jQueryShadow;
import {Component, ViewEncapsulation, OnInit, Input, OnChanges, SimpleChanges, ElementRef} from '@angular/core';
import {LoginService} from './login.service';
import {SettingsService} from './settings.sevices';
import {App} from './app';
import 'node_modules/candy/libs.min.js';
import 'node_modules/candy/candy.bundle.js';
import 'node_modules/candy-shop/notifyme/candy.js';
import 'node_modules/candy-shop/namecomplete/candy.js';
import 'node_modules/candy-shop/modify-role/candy.js';
import 'node_modules/candy-shop/me-does/candy.js';
import 'node_modules/candy-shop/notifications/candy.js';
import 'node_modules/candy-shop/refocus/candy.js';
import 'electron-cookies';
window['jQuery'] = jQueryOriginal;
declare const Candy: any, CandyShop: any, Base64: any;
Candy.Util.getPosLeftAccordingToWindowBounds = new Proxy(Candy.Util.getPosLeftAccordingToWindowBounds, {
apply(target, thisArg, argumentsList) {
argumentsList[1] -= shadow.host.getBoundingClientRect().left;
return target.apply(thisArg, argumentsList);
}
});
Candy.Util.getPosTopAccordingToWindowBounds = new Proxy(Candy.Util.getPosTopAccordingToWindowBounds, {
apply(target, thisArg, argumentsList) {
argumentsList[1] -= shadow.host.getBoundingClientRect().top;
return target.apply(thisArg, argumentsList);
}
});
@Component({
moduleId: module.id,
selector: 'candy',
templateUrl: 'candy.component.html',
styleUrls: ['candy.component.css'],
encapsulation: ViewEncapsulation.Native
})
export class CandyComponent implements OnInit, OnChanges {
@Input()
currentApp: App;
jid: string;
password: string;
nickname: string;
constructor (private loginService: LoginService, private settingsService: SettingsService, private element: ElementRef) {
}
ngOnInit () {
this.jid = this.loginService.user.username + '@mycard.moe';
this.password = this.loginService.user.external_id.toString();
this.nickname = this.loginService.user.username;
shadow = this.element.nativeElement.shadowRoot;
// 很 Tricky 的加载 Candy 的 css,这里涉及图片等资源的相对路径引用问题,如果丢给 Angular 去加载,会让相对路径找不到
const element = document.createElement('style');
element.innerHTML = `
@import "node_modules/candy/libs.min.css";
@import "node_modules/candy/res/default.css";
@import "node_modules/candy-shop/notifyme/candy.css";
@import "node_modules/candy-shop/namecomplete/candy.css";
@import "node_modules/candy-shop/modify-role/candy.css"
`;
shadow.insertBefore(element, shadow.firstChild);
// Candy fix
Base64.encode = (data: string) => Buffer.from(data).toString('base64');
Base64.decode = (data: string) => Buffer.from(data, 'base64').toString();
Candy.View.Template.Login.form = `
<form method="post" id="login-form" class="login-form">
<input type="hidden" id="nickname" name="nickname" value="' + this.nickname + '"/>
{{#displayUsername}}
<input type="hidden" id="username" name="username" value="' + this.jid + '"/>
{{#displayDomain}}
<span class="at-symbol">@</span>
<select id="domain" name="domain">{{#domains}}<option value="{{domain}}">{{domain}}</option>{{/domains}}</select>
{{/displayDomain}}
{{/displayUsername}}
{{#presetJid}}<input type="hidden" id="username" name="username" value="{{presetJid}}"/>{{/presetJid}}
{{#displayPassword}}<input type="hidden" id="password" name="password" value="' + this.password + '"/>{{/displayPassword}}
<input type="submit" class="button" value="{{_loginSubmit}}" />
</form>
`;
Candy.Util.setCookie('candy-nostatusmessages', '1', 365);
Candy.init('wss://chat.mycard.moe:5280/websocket', {
core: {
debug: false,
autojoin: this.currentApp.conference && [this.currentApp.conference + '@conference.mycard.moe'],
resource: 'mycard-' + Math.random().toString().split('.')[1]
},
view: {
assets: 'node_modules/candy/res/',
language: this.settingsService.getLocale().startsWith('zh') ? 'cn' : 'en',
enableXHTML: true,
}
});
CandyShop.NotifyMe.init();
CandyShop.NameComplete.init();
CandyShop.ModifyRole.init();
CandyShop.MeDoes.init();
CandyShop.Notifications.init();
CandyShop.Refocus.init();
Candy.Core.connect(this.jid, this.password, this.nickname);
}
ngOnChanges (changes: SimpleChanges): void {
if (!Candy.Core.getConnection()) {
return;
}
let conference = changes['currentApp'].currentValue.conference;
if (!conference) {
return;
}
conference += '@conference.mycard.moe';
if (Candy.View.Pane.Chat.rooms[conference]) {
Candy.View.Pane.Room.show(conference);
} else {
Candy.Core.Action.Jabber.Room.Join(conference);
}
}
}
/** /**
* Created by weijian on 2016/10/26. * Created by weijian on 2016/10/26.
*/ */
import {Injectable, NgZone, EventEmitter} from "@angular/core"; import {Injectable, NgZone, EventEmitter} from '@angular/core';
import {Http} from "@angular/http"; import {Http} from '@angular/http';
import {error} from "util"; // import {error} from 'util';
import Timer = NodeJS.Timer; // import Timer = NodeJS.Timer;
const Logger = { // const Logger = {
"error": (message: string) => { // 'error': (message: string) => {
console.error("DownloadService: ", message); // console.error('DownloadService: ', message);
} // }
}; // };
const Aria2 = require('aria2'); const Aria2 = require('aria2');
const MAX_LIST_NUM = 1000; const MAX_LIST_NUM = 1000;
...@@ -19,13 +19,13 @@ export class DownloadStatus { ...@@ -19,13 +19,13 @@ export class DownloadStatus {
completedLength: number; completedLength: number;
downloadSpeed: number; downloadSpeed: number;
get downloadSpeedText(): string { get downloadSpeedText (): string {
if (!isNaN(this.downloadSpeed) && this.downloadSpeed !== 0) { if (!isNaN(this.downloadSpeed) && this.downloadSpeed !== 0) {
const speedUnit = ["Byte/s", "KB/s", "MB/s", "GB/s", "TB/s"]; const speedUnit = ['Byte/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s'];
let currentUnit = Math.floor(Math.log(this.downloadSpeed) / Math.log(1024)); let currentUnit = Math.floor(Math.log(this.downloadSpeed) / Math.log(1024));
return (this.downloadSpeed / 1024 ** currentUnit).toFixed(1) + " " + speedUnit[currentUnit]; return (this.downloadSpeed / 1024 ** currentUnit).toFixed(1) + ' ' + speedUnit[currentUnit];
} }
return ""; return '';
}; };
gid: string; gid: string;
...@@ -35,22 +35,22 @@ export class DownloadStatus { ...@@ -35,22 +35,22 @@ export class DownloadStatus {
errorCode: string; errorCode: string;
errorMessage: string; errorMessage: string;
combine(...others: DownloadStatus[]): DownloadStatus { combine (...others: DownloadStatus[]): DownloadStatus {
const priority = { const priority = {
undefined: -1, undefined: -1,
"": -1, '': -1,
"active": 0, 'active': 0,
"complete": 0, 'complete': 0,
"paused": 1, 'paused': 1,
"waiting": 1, 'waiting': 1,
"removed": 2, 'removed': 2,
"error": 3 'error': 3
}; };
let status = Object.assign(new DownloadStatus(), this); let status = Object.assign(new DownloadStatus(), this);
for (let o of others) { for (let o of others) {
if (priority[o.status] > priority[status.status]) { if (priority[o.status] > priority[status.status]) {
status.status = o.status; status.status = o.status;
if (status.status === "error") { if (status.status === 'error') {
status.errorCode = o.errorCode; status.errorCode = o.errorCode;
status.errorMessage = o.errorMessage; status.errorMessage = o.errorMessage;
} }
...@@ -64,7 +64,7 @@ export class DownloadStatus { ...@@ -64,7 +64,7 @@ export class DownloadStatus {
} }
// 0相等. 1不想等 // 0相等. 1不想等
compareTo(other: DownloadStatus): number { compareTo (other: DownloadStatus): number {
if (this.status !== other.status || if (this.status !== other.status ||
this.downloadSpeed !== other.downloadSpeed || this.downloadSpeed !== other.downloadSpeed ||
this.completedLength !== other.completedLength || this.completedLength !== other.completedLength ||
...@@ -75,7 +75,7 @@ export class DownloadStatus { ...@@ -75,7 +75,7 @@ export class DownloadStatus {
} }
} }
constructor(item ?: any) { constructor (item ?: any) {
if (item) { if (item) {
this.completedLength = parseInt(item.completedLength) || 0; this.completedLength = parseInt(item.completedLength) || 0;
this.downloadSpeed = parseInt(item.downloadSpeed) || 0; this.downloadSpeed = parseInt(item.downloadSpeed) || 0;
...@@ -99,11 +99,11 @@ export class DownloadService { ...@@ -99,11 +99,11 @@ export class DownloadService {
open = this.aria2.open(); open = this.aria2.open();
updateEmitter = new EventEmitter<void>(); updateEmitter = new EventEmitter<void>();
downloadList: Map<string,DownloadStatus> = new Map(); downloadList: Map<string, DownloadStatus> = new Map();
taskMap: Map<string,string[]> = new Map(); taskMap: Map<string, string[]> = new Map();
async refreshDownloadList() { async refreshDownloadList () {
let activeList = await this.aria2.tellActive(); let activeList = await this.aria2.tellActive();
let waitList = await this.aria2.tellWaiting(0, MAX_LIST_NUM); let waitList = await this.aria2.tellWaiting(0, MAX_LIST_NUM);
let stoppedList = await this.aria2.tellStopped(0, MAX_LIST_NUM); let stoppedList = await this.aria2.tellStopped(0, MAX_LIST_NUM);
...@@ -120,17 +120,17 @@ export class DownloadService { ...@@ -120,17 +120,17 @@ export class DownloadService {
this.updateEmitter.emit(); this.updateEmitter.emit();
} }
constructor(private ngZone: NgZone, private http: Http) { constructor (private ngZone: NgZone, private http: Http) {
ngZone.runOutsideAngular(async() => { ngZone.runOutsideAngular(async () => {
await this.open; await this.open;
setInterval(async() => { setInterval(async () => {
await this.refreshDownloadList(); await this.refreshDownloadList();
}, ARIA2_INTERVAL); }, ARIA2_INTERVAL);
}) });
} }
private createId(): string { private createId (): string {
function s4() { function s4 () {
return Math.floor((1 + Math.random()) * 0x10000) return Math.floor((1 + Math.random()) * 0x10000)
.toString(16) .toString(16)
.substring(1); .substring(1);
...@@ -140,7 +140,7 @@ export class DownloadService { ...@@ -140,7 +140,7 @@ export class DownloadService {
s4() + '-' + s4() + s4() + s4(); s4() + '-' + s4() + s4() + s4();
} }
async progress(id: string, callback: (downloadStatus: DownloadStatus) => void) { async progress (id: string, callback: (downloadStatus: DownloadStatus) => void) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let gids = this.taskMap.get(id); let gids = this.taskMap.get(id);
if (gids) { if (gids) {
...@@ -154,7 +154,7 @@ export class DownloadService { ...@@ -154,7 +154,7 @@ export class DownloadService {
gids!.map((value, index, array) => { gids!.map((value, index, array) => {
let s = this.downloadList.get(value); let s = this.downloadList.get(value);
if (!s) { if (!s) {
throw "Gid not exists"; throw 'Gid not exists';
} }
return s; return s;
}) })
...@@ -164,13 +164,13 @@ export class DownloadService { ...@@ -164,13 +164,13 @@ export class DownloadService {
if (!allStatus) { if (!allStatus) {
allStatus = status; allStatus = status;
} else { } else {
if (allStatus.compareTo(status) != 0) { if (allStatus.compareTo(status) !== 0) {
allStatus = status; allStatus = status;
} }
} }
if (allStatus.status === "error") { if (allStatus.status === 'error') {
throw `Download Error: code ${allStatus.errorCode}, message: ${allStatus.errorMessage}`; throw `Download Error: code ${allStatus.errorCode}, message: ${allStatus.errorMessage}`;
} else if (allStatus.status === "complete") { } else if (allStatus.status === 'complete') {
resolve(); resolve();
subscription.unsubscribe(); subscription.unsubscribe();
} else { } else {
...@@ -183,12 +183,12 @@ export class DownloadService { ...@@ -183,12 +183,12 @@ export class DownloadService {
} }
}); });
} else { } else {
throw "Try to access invalid download id"; throw 'Try to access invalid download id';
} }
}) });
} }
async getFiles(id: string): Promise<string[]> { async getFiles (id: string): Promise<string[]> {
let gids = this.taskMap.get(id)!; let gids = this.taskMap.get(id)!;
let files: string[] = []; let files: string[] = [];
for (let gid of gids) { for (let gid of gids) {
...@@ -198,7 +198,7 @@ export class DownloadService { ...@@ -198,7 +198,7 @@ export class DownloadService {
return files; return files;
} }
async addMetalink(metalink: string, library: string): Promise<string> { async addMetalink (metalink: string, library: string): Promise<string> {
let encodedMeta4 = new Buffer((metalink)).toString('base64'); let encodedMeta4 = new Buffer((metalink)).toString('base64');
let gidList = await this.aria2.addMetalink(encodedMeta4, {dir: library}); let gidList = await this.aria2.addMetalink(encodedMeta4, {dir: library});
let taskId = this.createId(); let taskId = this.createId();
...@@ -208,7 +208,7 @@ export class DownloadService { ...@@ -208,7 +208,7 @@ export class DownloadService {
return taskId; return taskId;
} }
async addUri(url: string, destination: string): Promise<string> { async addUri (url: string, destination: string): Promise<string> {
await this.open; await this.open;
let taskId = this.createId(); let taskId = this.createId();
let gid = await this.aria2.addUri([url], {dir: destination}); let gid = await this.aria2.addUri([url], {dir: destination});
...@@ -216,10 +216,10 @@ export class DownloadService { ...@@ -216,10 +216,10 @@ export class DownloadService {
return taskId; return taskId;
} }
async pause(id: string): Promise<void> { async pause (id: string): Promise<void> {
await this.open; await this.open;
try { try {
await this.aria2.pause(id) await this.aria2.pause(id);
} catch (e) { } catch (e) {
} }
......
import {TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID} from "@angular/core"; import {TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID} from '@angular/core';
import {remote} from "electron"; import {remote} from 'electron';
export async function getTranslationProviders(): Promise<Object[]> { export async function getTranslationProviders (): Promise<Object[]> {
let locale = localStorage.getItem('locale'); let locale = localStorage.getItem('locale');
if (!locale) { if (!locale) {
locale = remote.app.getLocale(); locale = remote.app.getLocale();
...@@ -18,12 +18,12 @@ export async function getTranslationProviders(): Promise<Object[]> { ...@@ -18,12 +18,12 @@ export async function getTranslationProviders(): Promise<Object[]> {
{provide: TRANSLATIONS, useValue: translations}, {provide: TRANSLATIONS, useValue: translations},
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf'}, {provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
{provide: LOCALE_ID, useValue: locale} {provide: LOCALE_ID, useValue: locale}
] ];
} catch (error) { } catch (error) {
return noProviders return noProviders;
} }
} }
declare var System: any; declare const System: any;
function getTranslationsWithSystemJs(file: string) { function getTranslationsWithSystemJs (file: string) {
return System.import(file + '!text'); // relies on text plugin return System.import(file + '!text'); // relies on text plugin
} }
import {App} from "./app"; import {App} from './app';
import * as path from "path" import * as path from 'path';
/** /**
* Created by weijian on 2016/10/24. * Created by weijian on 2016/10/24.
*/ */
...@@ -9,17 +9,17 @@ export class InstallOption { ...@@ -9,17 +9,17 @@ export class InstallOption {
downloadFiles: string[]; downloadFiles: string[];
installLibrary: string; installLibrary: string;
get installDir(): string { get installDir (): string {
return path.join(this.installLibrary, this.app.id); return path.join(this.installLibrary, this.app.id);
} }
createShortcut: boolean; createShortcut: boolean;
createDesktopShortcut: boolean; createDesktopShortcut: boolean;
constructor(app: App, installLibrary = "", shortcut = false, desktopShortcut = false) { constructor (app: App, installLibrary = '', shortcut = false, desktopShortcut = false) {
this.app = app; this.app = app;
this.createShortcut = shortcut; this.createShortcut = shortcut;
this.createDesktopShortcut = desktopShortcut; this.createDesktopShortcut = desktopShortcut;
this.installLibrary = installLibrary; this.installLibrary = installLibrary;
} }
} }
\ No newline at end of file
:host { :host {
display: flex; display: flex;
height: 100%;
} }
#right { #right {
display: flex; display: flex;
...@@ -12,10 +13,11 @@ ...@@ -12,10 +13,11 @@
flex-grow: 1; flex-grow: 1;
} }
#candy { #candy-wrapper {
background-color: #444; background-color: #444;
height: 230px; height: 230px;
flex-shrink: 0; flex-shrink: 0;
position: relative;
} }
roster { roster {
...@@ -117,15 +119,21 @@ span { ...@@ -117,15 +119,21 @@ span {
float: right; float: right;
} }
.sidebar { #nav-wrapper {
width: 190px; width: 190px;
height: 100%;
flex-shrink: 0; flex-shrink: 0;
background-color: #f7f7f9; background-color: #f7f7f9;
border-right: 1px solid #eee; border-right: 1px solid #eee;
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
padding-top: 20px; padding-top: 20px;
/*resize: horizontal;*/
position: relative;
}
nav {
height: 100%;
} }
.sidebar .nav { .sidebar .nav {
...@@ -181,4 +189,44 @@ input.search::-webkit-input-placeholder{ ...@@ -181,4 +189,44 @@ input.search::-webkit-input-placeholder{
.icon { .icon {
width: 16px; width: 16px;
height: 16px; height: 16px;
}
nav::-webkit-resizer {
/*width: 100px;*/
/*height: 100px;*/
/*background-color: red;*/
border: 2px solid yellow;
background: blue;
box-shadow: 0 0 2px 5px red;
outline: 2px dashed green;
/*size does not work*/
display: block;
width: 150px !important;
height: 150px !important;
position: fixed;
}
.resize-wrapper {
position: relative;
}
.resize {
position: absolute;
}
.resize-right .resize {
width: 4px;
right: -2px;
top: 0;
bottom: 0;
cursor: col-resize;
}
.resize-top .resize {
height: 4px;
top: -2px;
left: 0;
right: 0;
cursor: row-resize;
} }
\ No newline at end of file
<!-- Begin page content --> <!-- Begin page content -->
<nav id="apps" *ngIf="apps" class="bg-faded sidebar scroll"> <div #nav id="nav-wrapper" class="resize-wrapper resize-right">
<div id="search" class="input-group"> <nav id="apps" *ngIf="apps" class="bg-faded sidebar scroll">
<i class="fa fa-search input-group-addon search" id="basic-addon1"></i> <div id="search" class="input-group">
<input type="text" class="form-control search" placeholder="搜索游戏" aria-describedby="basic-addon1"> <i class="fa fa-search input-group-addon search" id="basic-addon1"></i>
</div> <input type="text" class="form-control search" placeholder="搜索游戏" aria-describedby="basic-addon1">
</div>
<span i18n *ngIf="grouped_apps.installed">已安装</span> <span i18n *ngIf="grouped_apps.installed">已安装</span>
<ul *ngIf="grouped_apps.installed" class="nav nav-pills flex-column"> <ul *ngIf="grouped_apps.installed" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.installed" class="nav-item"> <li *ngFor="let app of grouped_apps.installed" class="nav-item">
<a (click)="chooseApp(app)" class="nav-link" [class.active]="app===currentApp" href="#"> <a (click)="chooseApp(app)" class="nav-link" [class.active]="app===currentApp" href="#">
<!--<img class="icon" src="https://lh3.googleusercontent.com/-crYEtoQ-4Ho/AAAAAAAAAAI/AAAAAAAAAAA/AKB_U8u0CDmxkVqQgOKesrJIb-6eiXacgA/s32-c-mo/photo.jpg">--> <!--<img class="icon" src="https://lh3.googleusercontent.com/-crYEtoQ-4Ho/AAAAAAAAAAI/AAAAAAAAAAA/AKB_U8u0CDmxkVqQgOKesrJIb-6eiXacgA/s32-c-mo/photo.jpg">-->{{app.name}}<i *ngIf="!app.isReady() && !app.status.total" class="spin fa fa-circle-o-notch fa-spin fa-fw"></i>
{{app.name}}<i <div *ngIf="!app.isReady() && app.status.total" class="progress">
*ngIf="!app.isReady() && !app.status.total" class="spin fa fa-circle-o-notch fa-spin fa-fw"></i> <div class="pie" [class.second-half]="app.status.progress/app.status.total>0.5">
<div *ngIf="!app.isReady() && app.status.total" class="progress"> <div class="left-side half-circle" [style.transform]="'rotate('+(app.status.progress/app.status.total).toString()+'turn)'"></div>
<div class="pie" [class.second-half]="app.status.progress/app.status.total>0.5"> <div class="right-side half-circle"></div>
<div class="left-side half-circle" </div>
[style.transform]="'rotate('+(app.status.progress/app.status.total).toString()+'turn)'"></div> <div class="shadow"></div>
<div class="right-side half-circle"></div>
</div> </div>
<div class="shadow"></div> </a>
</div> </li>
</a> </ul>
</li> <span i18n *ngIf="grouped_apps.recommend">推荐</span>
</ul> <ul *ngIf="grouped_apps.recommend" class="nav nav-pills flex-column">
<span i18n *ngIf="grouped_apps.recommend">推荐</span> <li *ngFor="let app of grouped_apps.recommend" class="nav-item">
<ul *ngIf="grouped_apps.recommend" class="nav nav-pills flex-column"> <a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a>
<li *ngFor="let app of grouped_apps.recommend" class="nav-item"> </li>
<a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a> </ul>
</li> <span i18n *ngIf="grouped_apps.mysterious">迷之物体</span>
</ul> <ul *ngIf="grouped_apps.mysterious" class="nav nav-pills flex-column">
<span i18n *ngIf="grouped_apps.mysterious">迷之物体</span> <li *ngFor="let app of grouped_apps.mysterious" class="nav-item">
<ul *ngIf="grouped_apps.mysterious" class="nav nav-pills flex-column"> <a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a>
<li *ngFor="let app of grouped_apps.mysterious" class="nav-item"> </li>
<a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a> </ul>
</li> <span i18n *ngIf="grouped_apps.touhou">东方 Project</span>
</ul> <ul *ngIf="grouped_apps.touhou" class="nav nav-pills flex-column">
<span i18n *ngIf="grouped_apps.touhou">东方 Project</span> <li *ngFor="let app of grouped_apps.touhou" class="nav-item">
<ul *ngIf="grouped_apps.touhou" class="nav nav-pills flex-column"> <a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a>
<li *ngFor="let app of grouped_apps.touhou" class="nav-item"> </li>
<a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a> </ul>
</li> <span i18n *ngIf="grouped_apps.touhou_pc98">东方旧作</span>
</ul> <ul *ngIf="grouped_apps.touhou_pc98" class="nav nav-pills flex-column">
<span i18n *ngIf="grouped_apps.touhou_pc98">东方旧作</span> <li *ngFor="let app of grouped_apps.touhou_pc98" class="nav-item">
<ul *ngIf="grouped_apps.touhou_pc98" class="nav nav-pills flex-column"> <a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a>
<li *ngFor="let app of grouped_apps.touhou_pc98" class="nav-item"> </li>
<a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a> </ul>
</li> <span i18n *ngIf="grouped_apps.runtime_installed">已安装的运行库</span>
</ul> <ul *ngIf="grouped_apps.runtime_installed" class="nav nav-pills flex-column">
<span i18n *ngIf="grouped_apps.runtime_installed">已安装的运行库</span> <li *ngFor="let app of grouped_apps.runtime_installed" class="nav-item">
<ul *ngIf="grouped_apps.runtime_installed" class="nav nav-pills flex-column"> <a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a>
<li *ngFor="let app of grouped_apps.runtime_installed" class="nav-item"> </li>
<a (click)="chooseApp(app)" href="#" class="nav-link" [class.active]="app===currentApp">{{app.name}}</a> </ul>
</li> </nav>
</ul> <div class="resize" (mousedown)="mousedown($event)"></div>
</nav> </div>
<div id="right"> <div id="right">
<div id="main"> <div id="main">
<app-detail *ngIf="currentApp" [currentApp]="currentApp"></app-detail> <app-detail *ngIf="currentApp" [currentApp]="currentApp"></app-detail>
<roster class="scroll"></roster> <roster class="scroll"></roster>
</div> </div>
<webview *ngIf="currentApp" #candy id="candy" [src]="candy_url" (new-window)="openExternal($event.url)" <div id="candy-wrapper" class="resize-wrapper resize-top">
nodeintegration></webview> <div class="resize" (mousedown)="mousedown($event)"></div>
<candy *ngIf="currentApp" [currentApp]="currentApp"></candy>
</div>
</div> </div>
/** /**
* Created by zh99998 on 16/9/2. * Created by zh99998 on 16/9/2.
*/ */
import {Component, OnInit, ElementRef, ViewChild} from "@angular/core"; import {Component, OnInit} from '@angular/core';
import {AppsService} from "./apps.service"; import {AppsService} from './apps.service';
import {LoginService} from "./login.service"; import {LoginService} from './login.service';
import {App, Category} from "./app"; import {App, Category} from './app';
import {URLSearchParams} from "@angular/http"; import {shell} from 'electron';
import {shell} from "electron"; import {SettingsService} from './settings.sevices';
import {SettingsService} from "./settings.sevices";
import WebViewElement = Electron.WebViewElement;
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
...@@ -18,60 +16,73 @@ import WebViewElement = Electron.WebViewElement; ...@@ -18,60 +16,73 @@ import WebViewElement = Electron.WebViewElement;
}) })
export class LobbyComponent implements OnInit { export class LobbyComponent implements OnInit {
@ViewChild('candy')
candy?: ElementRef;
candy_url: URL;
currentApp: App; currentApp: App;
private apps: Map<string,App>; private apps: Map<string, App>;
resizing: HTMLElement | undefined;
offset: number;
constructor(private appsService: AppsService, private loginService: LoginService, private settingsService: SettingsService) { constructor (private appsService: AppsService, private loginService: LoginService, private settingsService: SettingsService) {
} }
async ngOnInit() { async ngOnInit () {
this.apps = await this.appsService.loadApps(); this.apps = await this.appsService.loadApps();
if (this.apps.size > 0) { if (this.apps.size > 0) {
this.chooseApp(this.appsService.lastVisited || this.apps.get("ygopro")!); this.chooseApp(this.appsService.lastVisited || this.apps.get('ygopro')!);
// 初始化聊天室
let url = new URL('candy.html', location.href);
let params: URLSearchParams = url['searchParams']; // TypeScrpt 缺了 url.searchParams 的定义
params.set('jid', this.loginService.user.username + '@mycard.moe');
params.set('password', this.loginService.user.external_id.toString());
params.set('nickname', this.loginService.user.username);
switch (this.settingsService.getLocale()) {
case 'zh-CN':
params.set('language', 'cn');
break;
default:
params.set('language', 'en');
}
if (this.currentApp.conference) {
params.set('autojoin', this.currentApp.conference + '@conference.mycard.moe');
}
this.candy_url = url;
await this.appsService.migrate(); await this.appsService.migrate();
for (let app of this.apps.values()) { for (let app of this.apps.values()) {
await this.appsService.update(app); await this.appsService.update(app);
} }
} else { } else {
if (confirm("获取程序列表失败,是否重试?")) { if (confirm('获取程序列表失败,是否重试?')) {
location.reload(); location.reload();
} else { } else {
window.close(); window.close();
} }
} }
document.addEventListener('mousemove', (event: MouseEvent) => {
if (!this.resizing) {
return;
}
if (this.resizing.classList.contains('resize-right')) {
let width = this.offset + event.clientX;
if (width < 190) {
width = 190;
}
this.resizing.style.width = `${width}px`;
} else {
let height = this.offset - event.clientY;
if (height < 236) {
height = 236;
}
this.resizing.style.height = `${height}px`;
}
});
document.addEventListener('mouseup', (event: MouseEvent) => {
this.resizing = undefined;
});
} }
chooseApp(app: App) { mousedown (event: MouseEvent) {
// console.log(()
this.resizing = <HTMLElement>(<HTMLElement>event.target).parentNode;
if (this.resizing.classList.contains('resize-right')) {
this.offset = this.resizing.offsetWidth - event.clientX;
} else {
this.offset = this.resizing.offsetHeight + event.clientY;
}
}
chooseApp (app: App) {
this.currentApp = app; this.currentApp = app;
this.appsService.lastVisited = app; this.appsService.lastVisited = app;
if (this.candy && this.currentApp.conference) {
(<WebViewElement>this.candy.nativeElement).send('join', this.currentApp.conference + '@conference.mycard.moe');
}
} }
get grouped_apps() { get grouped_apps () {
let contains = ["game", "music", "book"].map((value) => Category[value]); let contains = ['game', 'music', 'book'].map((value) => Category[value]);
let result = {runtime: []}; let result = {runtime: []};
for (let app of this.apps.values()) { for (let app of this.apps.values()) {
let tag: string; let tag: string;
...@@ -90,14 +101,14 @@ export class LobbyComponent implements OnInit { ...@@ -90,14 +101,14 @@ export class LobbyComponent implements OnInit {
} }
if (!result[tag]) { if (!result[tag]) {
result[tag] = [] result[tag] = [];
} }
result[tag].push(app) result[tag].push(app);
} }
return result return result;
} }
openExternal(url: string) { openExternal (url: string) {
shell.openExternal(url); shell.openExternal(url);
} }
} }
/** /**
* Created by zh99998 on 16/9/2. * Created by zh99998 on 16/9/2.
*/ */
import {Component} from "@angular/core"; import {Component} from '@angular/core';
import {LoginService} from "./login.service"; import {LoginService} from './login.service';
import * as crypto from "crypto"; import * as crypto from 'crypto';
import * as querystring from "querystring"; import * as querystring from 'querystring';
import * as url from "url"; import * as url from 'url';
import {shell} from "electron"; import {shell} from 'electron';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
...@@ -18,9 +18,10 @@ export class LoginComponent { ...@@ -18,9 +18,10 @@ export class LoginComponent {
url: string; url: string;
return_sso_url = 'https://mycard.moe/login_callback'; // 这个url不会真的被使用,可以填写不存在的 return_sso_url = 'https://mycard.moe/login_callback'; // 这个url不会真的被使用,可以填写不存在的
constructor(private loginService: LoginService) { constructor (private loginService: LoginService) {
let payload = new Buffer(querystring.stringify({ let payload = new Buffer(querystring.stringify({
//nonce: nonce, // nonce: nonce,
return_sso_url: this.return_sso_url return_sso_url: this.return_sso_url
})).toString('base64'); })).toString('base64');
...@@ -28,16 +29,14 @@ export class LoginComponent { ...@@ -28,16 +29,14 @@ export class LoginComponent {
'sso': payload, 'sso': payload,
'sig': crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex') 'sig': crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex')
}); });
this.url = "https://ygobbs.com/session/sso_provider?" + request; this.url = 'https://ygobbs.com/session/sso_provider?' + request;
if (this.loginService.logging_out) { if (this.loginService.logging_out) {
let request = querystring.stringify({ this.url = 'https://ygobbs.com/logout?' + querystring.stringify({'redirect': this.url});
'redirect': this.url
});
this.url = "https://ygobbs.com/logout?" + request;
} }
} }
return_sso(return_url: string) { return_sso (return_url: string) {
if (!return_url.startsWith(this.return_sso_url)) { if (!return_url.startsWith(this.return_sso_url)) {
return; return;
} }
...@@ -47,7 +46,7 @@ export class LoginComponent { ...@@ -47,7 +46,7 @@ export class LoginComponent {
this.loginService.login(user); this.loginService.login(user);
} }
openExternal(url: string) { openExternal (url: string) {
shell.openExternal(url); shell.openExternal(url);
} }
} }
/** /**
* Created by zh99998 on 2016/10/25. * Created by zh99998 on 2016/10/25.
*/ */
import {Injectable} from "@angular/core"; import {Injectable} from '@angular/core';
import {Http} from "@angular/http"; import {Http} from '@angular/http';
export interface User { export interface User {
admin: boolean; admin: boolean;
...@@ -20,7 +20,7 @@ export class LoginService { ...@@ -20,7 +20,7 @@ export class LoginService {
logged_in = false; logged_in = false;
logging_out = false; logging_out = false;
constructor(private http: Http) { constructor (private http: Http) {
let data = localStorage.getItem('login'); let data = localStorage.getItem('login');
if (data) { if (data) {
this.user = JSON.parse(data); this.user = JSON.parse(data);
...@@ -28,16 +28,16 @@ export class LoginService { ...@@ -28,16 +28,16 @@ export class LoginService {
} }
} }
login(user: User) { login (user: User) {
this.user = user; this.user = user;
this.logged_in = true; this.logged_in = true;
localStorage.setItem('login', JSON.stringify(user)); localStorage.setItem('login', JSON.stringify(user));
} }
logout() { logout () {
this.logging_out = true; this.logging_out = true;
this.logged_in = false; this.logged_in = false;
localStorage.removeItem('login'); localStorage.removeItem('login');
} }
} }
\ No newline at end of file
import {MyCardNgFactory} from "../aot/app/mycard.module.ngfactory"; // import {MyCardNgFactory} from '../aot/app/mycard.module.ngfactory';
import {getTranslationProviders} from "./i18n-providers"; // import {getTranslationProviders} from './i18n-providers';
import {enableProdMode} from "@angular/core"; // import {enableProdMode} from '@angular/core';
import {platformBrowser} from "@angular/platform-browser"; // import {platformBrowser} from '@angular/platform-browser';
enableProdMode(); // enableProdMode();
//
getTranslationProviders().then(providers => { // getTranslationProviders().then(providers => {
const options = {providers}; // const options = {providers};
platformBrowser().bootstrapModuleFactory(MyCardNgFactory); // platformBrowser().bootstrapModuleFactory(MyCardNgFactory);
}); // });
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic"; window['jQuery'] = require('jquery');
import {getTranslationProviders} from "./i18n-providers"; window['Tether'] = require('tether');
import {MyCard} from "./mycard.module"; import 'node_modules/bootstrap/dist/js/bootstrap.min.js';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {getTranslationProviders} from './i18n-providers';
import {MyCard} from './mycard.module';
getTranslationProviders().then(providers => { getTranslationProviders().then(providers => {
const options = {providers}; const options = {providers};
platformBrowserDynamic().bootstrapModule(MyCard, options); platformBrowserDynamic().bootstrapModule(MyCard, options);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
</li> </li>
<!--<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'store'}" class="nav-item">--> <!--<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'store'}" class="nav-item">-->
<!--<a (click)="currentPage = 'store'" class="nav-link" href="#">商店</a>--> <!--<a (click)="currentPage = 'store'" class="nav-link" href="#">商店</a>-->
<!--</li>--> <!--</li>-->
<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'lobby'}" class="nav-item"> <li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'lobby'}" class="nav-item">
...@@ -64,8 +64,8 @@ ...@@ -64,8 +64,8 @@
<label i18n for="locale" class="col-sm-2 col-form-label">语言</label> <label i18n for="locale" class="col-sm-2 col-form-label">语言</label>
<div class="col-sm-10"> <div class="col-sm-10">
<select class="form-control" id="locale" [(ngModel)]="locale" name="locale"> <select class="form-control" id="locale" [(ngModel)]="locale" name="locale">
<option i18n value="en-US">英文</option> <option value="en-US">English</option>
<option i18n value="zh-CN">简体中文</option> <option value="zh-CN">简体中文</option>
</select> </select>
</div> </div>
</div> </div>
......
import {Component, Renderer, ChangeDetectorRef, OnInit, ElementRef, ViewChild} from "@angular/core"; import {Component, Renderer, ChangeDetectorRef, OnInit, ElementRef, ViewChild} from '@angular/core';
import {remote, shell} from "electron"; import {remote, shell} from 'electron';
import {LoginService} from "./login.service"; import {LoginService} from './login.service';
import {SettingsService} from "./settings.sevices"; import {SettingsService} from './settings.sevices';
import * as $ from 'jquery';
const autoUpdater: Electron.AutoUpdater = remote.getGlobal('autoUpdater'); const autoUpdater: Electron.AutoUpdater = remote.getGlobal('autoUpdater');
declare const $: any;
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
...@@ -13,7 +13,7 @@ declare const $: any; ...@@ -13,7 +13,7 @@ declare const $: any;
}) })
export class MyCardComponent implements OnInit { export class MyCardComponent implements OnInit {
currentPage: string = "lobby"; currentPage: string = 'lobby';
update_status: string | undefined = remote.getGlobal('update_status'); update_status: string | undefined = remote.getGlobal('update_status');
update_error: string | undefined; update_error: string | undefined;
...@@ -32,7 +32,9 @@ export class MyCardComponent implements OnInit { ...@@ -32,7 +32,9 @@ export class MyCardComponent implements OnInit {
locale: string; locale: string;
ngOnInit() { resizing: HTMLElement | null;
ngOnInit () {
this.update_elements = new Map(Object.entries({ this.update_elements = new Map(Object.entries({
'error': this.error, 'error': this.error,
'checking-for-update': this.checking_for_update, 'checking-for-update': this.checking_for_update,
...@@ -41,7 +43,8 @@ export class MyCardComponent implements OnInit { ...@@ -41,7 +43,8 @@ export class MyCardComponent implements OnInit {
})); }));
} }
constructor(private renderer: Renderer, private loginService: LoginService, private ref: ChangeDetectorRef, private settingsService: SettingsService) { constructor (private renderer: Renderer, private loginService: LoginService, private ref: ChangeDetectorRef,
private settingsService: SettingsService) {
// renderer.listenGlobal('window', 'message', (event) => { // renderer.listenGlobal('window', 'message', (event) => {
// console.log(event); // console.log(event);
// // Do something with 'event' // // Do something with 'event'
...@@ -70,20 +73,20 @@ export class MyCardComponent implements OnInit { ...@@ -70,20 +73,20 @@ export class MyCardComponent implements OnInit {
} }
update_retry() { update_retry () {
autoUpdater.checkForUpdates() autoUpdater.checkForUpdates();
} }
update_install() { update_install () {
autoUpdater.quitAndInstall() autoUpdater.quitAndInstall();
} }
set_update_status(status: string) { set_update_status (status: string) {
console.log('autoUpdater', status); console.log('autoUpdater', status);
if (this.update_status) { if (this.update_status) {
let element = this.update_elements.get(this.update_status); let element = this.update_elements.get(this.update_status);
if (element) { if (element) {
$(element.nativeElement).tooltip('dispose') $(element.nativeElement).tooltip('dispose');
} }
} }
this.update_status = status; this.update_status = status;
...@@ -91,19 +94,19 @@ export class MyCardComponent implements OnInit { ...@@ -91,19 +94,19 @@ export class MyCardComponent implements OnInit {
let element = this.update_elements.get(this.update_status); let element = this.update_elements.get(this.update_status);
if (element) { if (element) {
$(element.nativeElement).tooltip({placement: 'bottom', container: 'body'}) $(element.nativeElement).tooltip({placement: 'bottom', container: 'body'});
} }
} }
openExternal(url: string) { openExternal (url: string) {
shell.openExternal(url); shell.openExternal(url);
} }
submit() { submit () {
if (this.locale != this.settingsService.getLocale()) { if (this.locale !== this.settingsService.getLocale()) {
localStorage.setItem(SettingsService.SETTING_LOCALE, this.locale); localStorage.setItem(SettingsService.SETTING_LOCALE, this.locale);
remote.app.relaunch(); remote.app.relaunch();
remote.app.quit() remote.app.quit();
} }
} }
} }
import {NgModule, NO_ERRORS_SCHEMA} from "@angular/core"; import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import {BrowserModule} from "@angular/platform-browser"; import {BrowserModule} from '@angular/platform-browser';
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {HttpModule} from "@angular/http"; import {HttpModule} from '@angular/http';
import {MyCardComponent} from "./mycard.component"; import {MyCardComponent} from './mycard.component';
import {LoginComponent} from "./login.component"; import {LoginComponent} from './login.component';
import {StoreComponent} from "./store.component"; import {StoreComponent} from './store.component';
import {LobbyComponent} from "./lobby.component"; import {LobbyComponent} from './lobby.component';
import {AppDetailComponent} from "./app-detail.component"; import {AppDetailComponent} from './app-detail.component';
import {RosterComponent} from "./roster.component"; import {RosterComponent} from './roster.component';
import {YGOProComponent} from "./ygopro.component"; import {YGOProComponent} from './ygopro.component';
import {AppsService} from "./apps.service"; import {AppsService} from './apps.service';
import {SettingsService} from "./settings.sevices"; import {SettingsService} from './settings.sevices';
import {LoginService} from "./login.service"; import {LoginService} from './login.service';
import {DownloadService} from "./download.service"; import {DownloadService} from './download.service';
import {AboutComponent} from "./about.component"; import {AboutComponent} from './about.component';
import {CandyComponent} from './candy.component';
@NgModule({ @NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpModule], imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpModule],
declarations: [ declarations: [
MyCardComponent, LoginComponent, StoreComponent, LobbyComponent, MyCardComponent, LoginComponent, StoreComponent, LobbyComponent,
AppDetailComponent, RosterComponent, YGOProComponent, AboutComponent AppDetailComponent, RosterComponent, YGOProComponent, AboutComponent, CandyComponent
], ],
bootstrap: [MyCardComponent], bootstrap: [MyCardComponent],
providers: [ providers: [
...@@ -28,4 +29,4 @@ import {AboutComponent} from "./about.component"; ...@@ -28,4 +29,4 @@ import {AboutComponent} from "./about.component";
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}) })
export class MyCard { export class MyCard {
} }
\ No newline at end of file
/** /**
* Created by zh99998 on 16/9/2. * Created by zh99998 on 16/9/2.
*/ */
import {Component} from "@angular/core"; import {Component} from '@angular/core';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
selector: 'roster', selector: 'roster',
......
/** /**
* Created by weijian on 2016/10/24. * Created by weijian on 2016/10/24.
*/ */
import {Injectable} from "@angular/core"; import {Injectable} from '@angular/core';
import {remote} from "electron"; import {remote} from 'electron';
import * as path from "path"; import * as path from 'path';
export interface Library { export interface Library {
"default": boolean,path: string 'default': boolean;
path: string;
} }
@Injectable() @Injectable()
export class SettingsService { export class SettingsService {
static SETTING_LIBRARY = "library"; static SETTING_LIBRARY = 'library';
static defaultLibraries = [ static defaultLibraries = [
{ {
"default": true, 'default': true,
path: path.join(remote.app.getPath("appData"), "MyCardLibrary") path: path.join(remote.app.getPath('appData'), 'MyCardLibrary')
}, },
]; ];
static SETTING_LOCALE = 'locale';
static defaultLocale = remote.app.getLocale();
locale: string;
libraries: Library[]; libraries: Library[];
getLibraries() { getLibraries () {
if (!this.libraries) { if (!this.libraries) {
let data = localStorage.getItem(SettingsService.SETTING_LIBRARY); let data = localStorage.getItem(SettingsService.SETTING_LIBRARY);
if (!data) { if (!data) {
...@@ -36,7 +41,7 @@ export class SettingsService { ...@@ -36,7 +41,7 @@ export class SettingsService {
return this.libraries; return this.libraries;
} }
addLibrary(libraryPath: string, isDefault: boolean) { addLibrary (libraryPath: string, isDefault: boolean) {
let libraries = this.getLibraries(); let libraries = this.getLibraries();
if (isDefault) { if (isDefault) {
...@@ -44,37 +49,33 @@ export class SettingsService { ...@@ -44,37 +49,33 @@ export class SettingsService {
l.default = false; l.default = false;
}); });
} }
libraries.push({"default": isDefault, path: libraryPath}); libraries.push({'default': isDefault, path: libraryPath});
this.libraries = libraries; this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries)); localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
} }
setDefaultLibrary(library: Library) { setDefaultLibrary (library: Library) {
let libraries = this.getLibraries(); let libraries = this.getLibraries();
libraries.forEach((l) => { libraries.forEach((l) => {
l.default = library.path == l.path; l.default = library.path === l.path;
}); });
this.libraries = libraries; this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries)); localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
} }
getDefaultLibrary(): Library { getDefaultLibrary (): Library {
if (!this.libraries) { if (!this.libraries) {
this.getLibraries() this.getLibraries();
} }
let result = this.libraries.find((item) => item.default === true); let result = this.libraries.find((item) => item.default === true);
if (result) { if (result) {
return result return result;
} else { } else {
throw('no default library found') throw('no default library found');
} }
} }
static SETTING_LOCALE = "locale"; getLocale (): string {
static defaultLocale = remote.app.getLocale();
locale: string;
getLocale(): string {
if (!this.locale) { if (!this.locale) {
let locale = localStorage.getItem(SettingsService.SETTING_LOCALE); let locale = localStorage.getItem(SettingsService.SETTING_LOCALE);
if (!locale) { if (!locale) {
...@@ -87,8 +88,8 @@ export class SettingsService { ...@@ -87,8 +88,8 @@ export class SettingsService {
return this.locale; return this.locale;
} }
setLocale(locale: string) { setLocale (locale: string) {
this.locale = locale; this.locale = locale;
localStorage.setItem(SettingsService.SETTING_LOCALE, locale); localStorage.setItem(SettingsService.SETTING_LOCALE, locale);
} }
} }
\ No newline at end of file
...@@ -11,7 +11,7 @@ export class ComparableSet<T> extends Set<T> { ...@@ -11,7 +11,7 @@ export class ComparableSet<T> extends Set<T> {
} }
isSuperset(subset: Set<T>) { isSuperset(subset: Set<T>) {
for (var elem of subset) { for (let elem of subset) {
if (!this.has(elem)) { if (!this.has(elem)) {
return false; return false;
} }
...@@ -20,16 +20,16 @@ export class ComparableSet<T> extends Set<T> { ...@@ -20,16 +20,16 @@ export class ComparableSet<T> extends Set<T> {
} }
union(setB: Set<T>): Set<T> { union(setB: Set<T>): Set<T> {
var union = new Set(this); let union = new Set(this);
for (var elem of setB) { for (let elem of setB) {
union.add(elem); union.add(elem);
} }
return union; return union;
} }
intersection(setB: Set<T>): Set<T> { intersection(setB: Set<T>): Set<T> {
var intersection = new Set(); let intersection = new Set();
for (var elem of setB) { for (let elem of setB) {
if (this.has(elem)) { if (this.has(elem)) {
intersection.add(elem); intersection.add(elem);
} }
...@@ -38,8 +38,8 @@ export class ComparableSet<T> extends Set<T> { ...@@ -38,8 +38,8 @@ export class ComparableSet<T> extends Set<T> {
} }
difference(setB: Set<T>): Set<T> { difference(setB: Set<T>): Set<T> {
var difference = new Set(this); let difference = new Set(this);
for (var elem of setB) { for (let elem of setB) {
difference.delete(elem); difference.delete(elem);
} }
return difference; return difference;
......
/** /**
* Created by zh99998 on 16/9/2. * Created by zh99998 on 16/9/2.
*/ */
import {Component} from "@angular/core"; import {Component} from '@angular/core';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
selector: 'store', selector: 'store',
......
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Candy - Chats are not dead yet</title>
<link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="node_modules/candy/libs.min.css"/>
<link rel="stylesheet" type="text/css" href="node_modules/candy/res/default.css"/>
<style>
body {
font-family: -apple-system, Arial, 'Source Sans Pro', "Microsoft YaHei", 'Microsoft JhengHei', "WenQuanYi Micro Hei", sans-serif;
}
/* Turn on custom 8px wide scrollbar */
::-webkit-scrollbar {
width: 8px; /* 1px wider than Lion. */
/* This is more usable for users trying to click it. */
background-color: rgba(0,0,0,0);
-webkit-border-radius: 100px;
}
/* hover effect for both scrollbar area, and scrollbar 'thumb' */
::-webkit-scrollbar:active {
background-color: rgba(0, 0, 0, 0.05);
}
/* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */
::-webkit-scrollbar-thumb:vertical {
/* This is the EXACT color of Mac OS scrollbars.
Yes, I pulled out digital color meter */
background: rgba(0,0,0,0.1);
-webkit-border-radius: 100px;
}
::-webkit-scrollbar-thumb:vertical:active {
background: rgba(0,0,0,0.2); /* Some darker color when you click it */
-webkit-border-radius: 100px;
}
.scroll {
overflow-y: hidden;
}
.scroll:hover {
overflow-y: auto;
}
</style>
<script>delete module.exports</script>
<script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="node_modules/candy/libs.min.js"></script>
<script type="text/javascript" src="node_modules/candy/candy.min.js"></script>
<!-- plugins -->
<!--<script type="text/javascript" src="node_modules/candy-shop/replies/candy.js"></script>-->
<!--<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/replies/candy.css"/>-->
<script type="text/javascript" src="node_modules/candy-shop/notifyme/candy.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/notifyme/candy.css"/>
<script type="text/javascript" src="node_modules/candy-shop/namecomplete/candy.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/namecomplete/candy.css"/>
<script type="text/javascript" src="node_modules/candy-shop/modify-role/candy.js"></script>
<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/modify-role/candy.css"/>
<!-- 内联图片插件不好用 -->
<!--<script type="text/javascript" src="node_modules/candy-shop/inline-images/candy.js"></script>-->
<!--<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/inline-images/candy.css"/>-->
<script type="text/javascript" src="node_modules/candy-shop/me-does/candy.js"></script>
<script type="text/javascript" src="node_modules/candy-shop/notifications/candy.js"></script>
<script type="text/javascript" src="node_modules/candy-shop/refocus/candy.js"></script>
<!--<script type="text/javascript" src="node_modules/candy-shop/slash-commands/slash-commands.js"></script>-->
<!--正在输入那个插件不好用-->
<!--<script type="text/javascript" src="node_modules/candy-shop/typingnotifications/typingnotifications.js"></script>-->
<!--<link rel="stylesheet" type="text/css" href="node_modules/candy-shop/typingnotifications/typingnotifications.css" />-->
<style>
.message-pane-wrapper, .message-form-wrapper {
margin-right: 190px;
}
.roster-pane, #chat-toolbar{
width: 190px
}
</style>
<style>
#candy {
background-color: #f7f7f9;
}
#chat-tabs li {
box-shadow: 0 0 1px 1px #ccc;
}
#chat-tabs a {
color: #999;
background-color: #ececec;
}
#chat-tabs .active a.label {
background-color: white;
}
#chat-tabs .active .transition, #chat-tabs .transition {
background: none;
/*background-color: white;*/
}
.message-pane-wrapper{
background-color: white;
padding: 0;
}
.message-pane li {
border-bottom: none;
box-shadow: none;
padding: 0 5px;
}
.message-pane li>div {
padding: 0 0 0 150px;
line-height: 28px;
}
.message-pane .label {
margin-left: -150px;
width: 130px;
}
.roster-pane {
background-color: initial;
border-top: none;
box-shadow: none;
margin: 30px 0 32px 0;
}
.roster-pane .user {
border-bottom: none;
box-shadow: none;
color: black;
}
.roster-pane .user:hover {
background-color: #ebf3f8;
}
#chat-toolbar {
background-color: initial;
border-top: 1px solid #eee;
box-shadow: none;
}
.message-form-wrapper {
border-top: 1px solid #eee;
}
.roster-pane .label{
text-shadow: none
}
#candy {
border-top: 1px solid #eee;
box-shadow: inset 0 1px 2px white;
}
.usercount span {
background-color: initial;
color: #a7a7a7;
}
/*#chat-toolbar #emoticons-icon, #chat-toolbar .usercount {*/
/*background-image: initial;*/
/*}*/
/*#chat-toolbar #emoticons-icon::before, #chat-toolbar .usercount::before {*/
/*font-size: 16px;*/
/*}*/
</style>
</head>
<body>
<div id="candy"></div>
<script type="text/javascript">
const {remote, ipcRenderer} = require('electron');
// remote.getCurrentWebContents().openDevTools();
require('electron-cookies'); // https://github.com/hstove/electron-cookies
ipcRenderer.on('join', (event, message) => {
if (Candy.View.Pane.Chat.rooms[message]) {
Candy.View.Pane.Room.show(message);
} else {
Candy.Core.Action.Jabber.Room.Join(message);
}
});
// fix
Base64.encode = (data) => new Buffer(data).toString('base64');
Base64.decode = (data) => new Buffer(data, 'base64').toString();
// candy init
const params = new URLSearchParams(location.search);
Candy.View.Template.Login.form = '<form method="post" id="login-form" class="login-form">' + '<input type="hidden" id="nickname" name="nickname" value="' + params.get('nickname') + '"/>' + '{{#displayUsername}}<input type="hidden" id="username" name="username" value="' + params.get('jid') + '"/>' + '{{#displayDomain}} <span class="at-symbol">@</span> ' + '<select id="domain" name="domain">{{#domains}}<option value="{{domain}}">{{domain}}</option>{{/domains}}</select>' + "{{/displayDomain}}" + "{{/displayUsername}}" + '{{#presetJid}}<input type="hidden" id="username" name="username" value="{{presetJid}}"/>{{/presetJid}}' + '{{#displayPassword}}<input type="hidden" id="password" name="password" value="' + params.get('password') + '"/>{{/displayPassword}}' + '<input type="submit" class="button" value="{{_loginSubmit}}" /></form>';
// Candy.View.Template.Chat.toolbar = '<ul id="chat-toolbar">' + '<li id="emoticons-icon" class="fa fa-smile-o" data-tooltip="{{tooltipEmoticons}}"></li>' + '<li id="chat-sound-control" class="checked" data-tooltip="{{tooltipSound}}"></li>' + '<li class="checked" id="chat-statusmessage-control" data-tooltip="{{tooltipStatusmessage}}">' + '</li><li class="context" data-tooltip="{{tooltipAdministration}}"></li>' + '<li class="usercount fa fa-user-o" data-tooltip="{{tooltipUsercount}}">' + '<span id="chat-usercount"></span></li></ul>',
Candy.Util.setCookie('candy-nostatusmessages', '1', 365);
Candy.init('wss://chat.mycard.moe:5280/websocket', {
core: {
debug: false,
autojoin: params.get('autojoin') && [params.get('autojoin')],
resource: 'mycard-' + Math.random().toString().split('.')[1],
enableXHTML: true
},
view: {assets: 'node_modules/candy/res/', language: params.get('language')}
});
CandyShop.NotifyMe.init();
CandyShop.NameComplete.init();
CandyShop.ModifyRole.init();
CandyShop.MeDoes.init();
CandyShop.Notifications.init();
CandyShop.Refocus.init();
Candy.Core.connect(params.get('jid'), params.get('password'), params.get('nickname'));
</script>
</body>
</html>
...@@ -17,16 +17,12 @@ ...@@ -17,16 +17,12 @@
<script src="systemjs.config.js"></script> <script src="systemjs.config.js"></script>
<script>delete module.exports</script>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/tether/dist/js/tether.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script> <script>
System.import('app').catch((error) => { System.import('app').catch((error) => {
$('#loading').hide(); document.getElementById('loading').setAttribute('hidden', 'hidden');
$('#failed').removeAttr('hidden'); document.getElementById('failed').removeAttribute('hidden');
$('#error').removeAttr('hidden').text(error); document.getElementById('error').removeAttribute('hidden');
document.getElementById('error').textContent = error;
}); });
</script> </script>
</head> </head>
......
...@@ -141,6 +141,7 @@ function createWindow() { ...@@ -141,6 +141,7 @@ function createWindow() {
width: 1024, width: 1024,
height: 640, height: 640,
frame: process.platform == 'darwin', frame: process.platform == 'darwin',
transparent: true,
titleBarStyle: process.platform == 'darwin' ? 'hidden' : null titleBarStyle: process.platform == 'darwin' ? 'hidden' : null
}); });
......
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
"repository": "github:mycard/mycard", "repository": "github:mycard/mycard",
"scripts": { "scripts": {
"start": "tsc && electron .", "start": "tsc && electron .",
"lint": "tslint ./app/*.ts -t verbose",
"pack": "tsc && build --dir", "pack": "tsc && build --dir",
"dist": "tsc && build", "dist": "tsc && build",
"build:aot": "ngc -p tsconfig-aot.json && rollup -c rollup-config.js", "build:aot": "ngc -p tsconfig-aot.json && rollup -c rollup-config.js",
...@@ -24,9 +25,14 @@ ...@@ -24,9 +25,14 @@
"@angular/platform-browser": "latest", "@angular/platform-browser": "latest",
"@angular/platform-browser-dynamic": "latest", "@angular/platform-browser-dynamic": "latest",
"@angular/router": "latest", "@angular/router": "latest",
"@types/bootstrap": "latest",
"@types/jquery": "latest",
"@types/tether": "latest",
"angular-in-memory-web-api": "latest", "angular-in-memory-web-api": "latest",
"aria2": "latest", "aria2": "latest",
"bootstrap": "next", "bootstrap": "next",
"candy": "https://github.com/mycard/candy/releases/download/v2.2.0/candy.tar.gz",
"candy-shop": "zh99998/candy-plugins#patch-3",
"core-js": "latest", "core-js": "latest",
"electron-auto-updater": "latest", "electron-auto-updater": "latest",
"electron-cookies": "latest", "electron-cookies": "latest",
...@@ -38,14 +44,12 @@ ...@@ -38,14 +44,12 @@
"jquery": "latest", "jquery": "latest",
"raw-socket": "latest", "raw-socket": "latest",
"reflect-metadata": "latest", "reflect-metadata": "latest",
"rxjs": "5.0.0-beta.12", "rxjs": "latest",
"systemjs": "mycard/systemjs#mycard", "systemjs": "mycard/systemjs#mycard",
"systemjs-plugin-text": "latest", "systemjs-plugin-text": "latest",
"tether": "latest", "tether": "latest",
"vue": "latest", "vue": "latest",
"zone.js": "^0.6.26", "zone.js": "latest"
"candy": "https://github.com/mycard/candy/releases/download/v2.2.0/candy.tar.gz",
"candy-shop": "zh99998/candy-plugins#patch-2"
}, },
"devDependencies": { "devDependencies": {
"@angular/compiler-cli": "latest", "@angular/compiler-cli": "latest",
...@@ -61,7 +65,8 @@ ...@@ -61,7 +65,8 @@
"rollup-plugin-commonjs": "latest", "rollup-plugin-commonjs": "latest",
"rollup-plugin-node-resolve": "latest", "rollup-plugin-node-resolve": "latest",
"rollup-plugin-uglify": "latest", "rollup-plugin-uglify": "latest",
"typescript": "latest" "typescript": "latest",
"tslint": "^3.15.1"
}, },
"build": { "build": {
"productName": "MyCard", "productName": "MyCard",
......
...@@ -12,10 +12,14 @@ mycard { ...@@ -12,10 +12,14 @@ mycard {
flex-direction: column; flex-direction: column;
} }
.darwin #window-buttons { .darwin #window-buttons, .darwin #border {
display: none; display: none;
} }
.darwin #navbar {
padding-top: 1rem !important;
}
#window-buttons > i { #window-buttons > i {
color: #a7a7a7; color: #a7a7a7;
font-size: 18px; font-size: 18px;
...@@ -42,17 +46,14 @@ mycard { ...@@ -42,17 +46,14 @@ mycard {
margin: 0 0.3rem; margin: 0 0.3rem;
} }
.darwin #navbar {
padding-left: 80px;
}
/* Turn on custom 8px wide scrollbar */ /* Turn on custom 8px wide scrollbar */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; /* 1px wider than Lion. */ width: 8px; /* 1px wider than Lion. */
/* This is more usable for users trying to click it. */ /* This is more usable for users trying to click it. */
background-color: rgba(0,0,0,0); background-color: rgba(0, 0, 0, 0);
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
} }
/* hover effect for both scrollbar area, and scrollbar 'thumb' */ /* hover effect for both scrollbar area, and scrollbar 'thumb' */
::-webkit-scrollbar:active { ::-webkit-scrollbar:active {
background-color: rgba(0, 0, 0, 0.05); background-color: rgba(0, 0, 0, 0.05);
...@@ -62,11 +63,12 @@ mycard { ...@@ -62,11 +63,12 @@ mycard {
::-webkit-scrollbar-thumb:vertical { ::-webkit-scrollbar-thumb:vertical {
/* This is the EXACT color of Mac OS scrollbars. /* This is the EXACT color of Mac OS scrollbars.
Yes, I pulled out digital color meter */ Yes, I pulled out digital color meter */
background: rgba(0,0,0,0.1); background: rgba(0, 0, 0, 0.1);
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
} }
::-webkit-scrollbar-thumb:vertical:active { ::-webkit-scrollbar-thumb:vertical:active {
background: rgba(0,0,0,0.2); /* Some darker color when you click it */ background: rgba(0, 0, 0, 0.2); /* Some darker color when you click it */
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
} }
......
...@@ -72,7 +72,12 @@ System.config({ ...@@ -72,7 +72,12 @@ System.config({
"ini": "@node/ini", "ini": "@node/ini",
"mkdirp": "@node/mkdirp", "mkdirp": "@node/mkdirp",
"aria2": "@node/aria2", "aria2": "@node/aria2",
"electron-sudo": "@node/electron-sudo" "electron-sudo": "@node/electron-sudo",
"electron-cookies": "@node/electron-cookies",
'jquery': '@node/jquery',
'tether': '@node/tether',
'bootstrap': '@node/bootstrap'
}, },
// packages tells the System loader how to load when no filename and/or no extension // packages tells the System loader how to load when no filename and/or no extension
packages: { packages: {
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
"variables-before-functions" "variables-before-functions"
], ],
"no-arg": true, "no-arg": true,
"no-bitwise": true,
"no-console": [ "no-console": [
true, true,
"debug", "debug",
...@@ -62,8 +61,9 @@ ...@@ -62,8 +61,9 @@
true, true,
"single" "single"
], ],
"radix": true, "semicolon": [
"semicolon": true, "always"
],
"triple-equals": [ "triple-equals": [
true, true,
"allow-null-check" "allow-null-check"
...@@ -88,4 +88,4 @@ ...@@ -88,4 +88,4 @@
"check-type" "check-type"
] ]
} }
} }
\ No newline at end of file
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