tslint changes

This commit is contained in:
Jeremy Simpson
2019-08-30 01:52:40 -07:00
parent 7603879d35
commit 7f7efbb94b
446 changed files with 70650 additions and 70661 deletions
File diff suppressed because it is too large Load Diff
+215 -215
View File
@@ -5,247 +5,247 @@ import { Season, Holiday, MapType } from '../../common/interfaces';
import { getUrl } from '../../client/rev';
interface Track {
name: string;
src: string[];
howl?: Howl;
name: string;
src: string[];
howl?: Howl;
}
function getTracks(season: Season, holiday: Holiday, map: MapType) {
switch (map) {
case MapType.Island:
return [
'island',
'sunny-island',
];
case MapType.House:
return [
'happy-house',
'sweet-home',
];
case MapType.Cave:
return [
'cave-crystals',
'cave-secrets',
];
default:
return [
//'largo',
//'musicbox',
//'unrest',
'bossanova',
'clop',
'fivefour',
'hypnosis',
'scherzo',
'trills',
'waltzalt',
...(season === Season.Winter ? [
'trees-winter',
'reindeer-winter',
] : [
'trees',
'reindeer',
]),
'season',
'ambient',
'building',
'school',
'falling',
'tio',
'orchid',
...(season === Season.Winter ? [
'xmas-air',
'xmas-horns',
'xmas-presents',
] : []),
...(holiday === Holiday.Halloween ? [
'ghost',
'pumpkin',
] : []),
];
}
switch (map) {
case MapType.Island:
return [
'island',
'sunny-island',
];
case MapType.House:
return [
'happy-house',
'sweet-home',
];
case MapType.Cave:
return [
'cave-crystals',
'cave-secrets',
];
default:
return [
//'largo',
//'musicbox',
//'unrest',
'bossanova',
'clop',
'fivefour',
'hypnosis',
'scherzo',
'trills',
'waltzalt',
...(season === Season.Winter ? [
'trees-winter',
'reindeer-winter',
] : [
'trees',
'reindeer',
]),
'season',
'ambient',
'building',
'school',
'falling',
'tio',
'orchid',
...(season === Season.Winter ? [
'xmas-air',
'xmas-horns',
'xmas-presents',
] : []),
...(holiday === Holiday.Halloween ? [
'ghost',
'pumpkin',
] : []),
];
}
}
const FADE_TRACKS = true;
function fadeOut(track: Track, id: number, volume: number) {
const howl = track && track.howl;
const howl = track && track.howl;
if (howl) {
if (FADE_TRACKS) {
howl
.fade(volume, 0, 1000, id)
.once('fade', () => howl.pause(id).stop(id), id);
} else {
howl
.volume(0, id)
.pause(id)
.stop(id);
}
}
if (howl) {
if (FADE_TRACKS) {
howl
.fade(volume, 0, 1000, id)
.once('fade', () => howl.pause(id).stop(id), id);
} else {
howl
.volume(0, id)
.pause(id)
.stop(id);
}
}
}
function fadeIn(track: Track, id: number, volume: number) {
if (track && track.howl) {
track.howl.fade(0, volume, 1000, id);
}
if (track && track.howl) {
track.howl.fade(0, volume, 1000, id);
}
}
interface Instance {
id: number;
track: Track;
id: number;
track: Track;
}
@Injectable({ providedIn: 'root' })
export class Audio {
private tracks: Track[] = [];
private volume = 0;
private loops = 0;
private playing = false;
private stopped: Instance[] = [];
private instance?: Instance;
get trackName() {
return this.instance && this.volume ? this.instance.track.name : '';
}
initTracks(season: Season, holiday: Holiday, map: MapType) {
const tracks = getTracks(season, holiday, map);
private tracks: Track[] = [];
private volume = 0;
private loops = 0;
private playing = false;
private stopped: Instance[] = [];
private instance?: Instance;
get trackName() {
return this.instance && this.volume ? this.instance.track.name : '';
}
initTracks(season: Season, holiday: Holiday, map: MapType) {
const tracks = getTracks(season, holiday, map);
// Make new tracks more frequent
// const duplicateTracks = tracks.filter(t => t === 'ghost' || t === 'pumpkin');
// tracks.push(...duplicateTracks);
// tracks.push(...duplicateTracks);
// Make new tracks more frequent
// const duplicateTracks = tracks.filter(t => t === 'ghost' || t === 'pumpkin');
// tracks.push(...duplicateTracks);
// tracks.push(...duplicateTracks);
this.tracks = tracks.map(name => ({ name, src: [getUrl(`music/${name}.webm`), getUrl(`music/${name}.mp3`)] }));
this.loops = 0;
}
setVolume(volume: number) {
this.volume = volume / 100;
this.tracks = tracks.map(name => ({ name, src: [getUrl(`music/${name}.webm`), getUrl(`music/${name}.mp3`)] }));
this.loops = 0;
}
setVolume(volume: number) {
this.volume = volume / 100;
if (this.playing) {
if (this.instance) {
this.setInstanceVolume(this.instance, this.volume);
} else if (this.volume) {
this.playRandomTrack();
}
}
}
play() {
try {
if (!this.playing) {
this.playing = true;
if (this.playing) {
if (this.instance) {
this.setInstanceVolume(this.instance, this.volume);
} else if (this.volume) {
this.playRandomTrack();
}
}
}
play() {
try {
if (!this.playing) {
this.playing = true;
if (this.volume) {
if (this.instance) {
this.resumeInstance(this.instance);
} else {
this.playRandomTrack();
}
}
}
} catch (e) {
console.error(e);
}
}
playOrSwitchToRandomTrack() {
if (FADE_TRACKS) {
if (this.playing && this.volume) {
this.playRandomTrack();
} else {
this.play();
}
} else {
this.play();
}
}
stop() {
if (this.playing) {
this.playing = false;
this.stopInstance(this.instance);
}
}
forcePlay() {
if (!this.instance || !this.instance.track.howl!.playing(this.instance.id)) {
this.playRandomTrack();
}
}
touch() {
this.stopInstances();
this.setInstanceVolume(this.instance, this.volume);
}
private switchToTrack(track: Track) {
if (this.instance && this.instance.track === track) {
return false;
} else {
this.stopInstance(this.instance);
this.instance = this.playTrack(track);
return true;
}
}
playRandomTrack() {
while (!this.switchToTrack(sample(this.tracks)!))
;
if (this.volume) {
if (this.instance) {
this.resumeInstance(this.instance);
} else {
this.playRandomTrack();
}
}
}
} catch (e) {
console.error(e);
}
}
playOrSwitchToRandomTrack() {
if (FADE_TRACKS) {
if (this.playing && this.volume) {
this.playRandomTrack();
} else {
this.play();
}
} else {
this.play();
}
}
stop() {
if (this.playing) {
this.playing = false;
this.stopInstance(this.instance);
}
}
forcePlay() {
if (!this.instance || !this.instance.track.howl!.playing(this.instance.id)) {
this.playRandomTrack();
}
}
touch() {
this.stopInstances();
this.setInstanceVolume(this.instance, this.volume);
}
private switchToTrack(track: Track) {
if (this.instance && this.instance.track === track) {
return false;
} else {
this.stopInstance(this.instance);
this.instance = this.playTrack(track);
return true;
}
}
playRandomTrack() {
while (!this.switchToTrack(sample(this.tracks)!))
;
this.loops = random(4, 7);
}
private playTrack(track: Track): Instance {
this.prepareTrack(track);
const id = track.howl!.play();
fadeIn(track, id, this.volume);
return { id, track };
}
private resumeInstance({ track, id }: Instance) {
track.howl!.play(id);
fadeIn(track, id, this.volume);
}
private stopInstance(instance: Instance | undefined) {
if (instance) {
this.stopped.push(instance);
}
this.loops = random(4, 7);
}
private playTrack(track: Track): Instance {
this.prepareTrack(track);
const id = track.howl!.play();
fadeIn(track, id, this.volume);
return { id, track };
}
private resumeInstance({ track, id }: Instance) {
track.howl!.play(id);
fadeIn(track, id, this.volume);
}
private stopInstance(instance: Instance | undefined) {
if (instance) {
this.stopped.push(instance);
}
this.stopInstances();
}
private stopInstances() {
this.stopped.forEach(({ track, id }) => fadeOut(track, id, this.volume));
this.stopped = this.stopped.filter(({ track, id }) => track.howl!.playing(id));
}
private setInstanceVolume(instance: Instance | undefined, volume: number) {
if (instance) {
const howl = instance.track.howl!;
howl.volume(volume, instance.id);
this.stopInstances();
}
private stopInstances() {
this.stopped.forEach(({ track, id }) => fadeOut(track, id, this.volume));
this.stopped = this.stopped.filter(({ track, id }) => track.howl!.playing(id));
}
private setInstanceVolume(instance: Instance | undefined, volume: number) {
if (instance) {
const howl = instance.track.howl!;
howl.volume(volume, instance.id);
if (volume && !howl.playing(instance.id)) {
howl.play(instance.id);
} else if (!volume && howl.playing(instance.id)) {
howl.pause(instance.id);
}
}
}
private prepareTrack(track: Track) {
if (!track.howl) {
track.howl = new Howl({
src: track.src,
loop: true,
html5: true,
});
if (volume && !howl.playing(instance.id)) {
howl.play(instance.id);
} else if (!volume && howl.playing(instance.id)) {
howl.pause(instance.id);
}
}
}
private prepareTrack(track: Track) {
if (!track.howl) {
track.howl = new Howl({
src: track.src,
loop: true,
html5: true,
});
track.howl.on('end', id => this.onEnd(id));
}
}
private handlingOnEnd = 0;
private handlingOnEndAt = 0;
private onEnd(id: number) {
if (
this.instance && this.instance.id === id && --this.loops < 0 &&
(this.handlingOnEnd !== id || this.handlingOnEndAt < performance.now())
) {
this.handlingOnEnd = id;
this.handlingOnEndAt = performance.now() + 500;
track.howl.on('end', id => this.onEnd(id));
}
}
private handlingOnEnd = 0;
private handlingOnEndAt = 0;
private onEnd(id: number) {
if (
this.instance && this.instance.id === id && --this.loops < 0 &&
(this.handlingOnEnd !== id || this.handlingOnEndAt < performance.now())
) {
this.handlingOnEnd = id;
this.handlingOnEndAt = performance.now() + 500;
if (this.volume && this.playing) {
this.playRandomTrack();
} else {
this.stopInstance(this.instance);
}
}
}
if (this.volume && this.playing) {
this.playRandomTrack();
} else {
this.stopInstance(this.instance);
}
}
}
}
+14 -14
View File
@@ -3,20 +3,20 @@ import { Router, CanActivate } from '@angular/router';
import { Model } from './model';
@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private model: Model) {
}
canActivate() {
return this.model.accountPromise
.then(account => {
if (account) {
return true;
} else {
this.router.navigate(['/']);
return false;
}
}) as any;
}
constructor(private router: Router, private model: Model) {
}
canActivate() {
return this.model.accountPromise
.then(account => {
if (account) {
return true;
} else {
this.router.navigate(['/']);
return false;
}
}) as any;
}
}
+33 -33
View File
@@ -4,44 +4,44 @@ import { Person } from '../../common/rollbar';
@Injectable()
export class ErrorReporter {
disable() {
}
configureUser(_person: Person) {
}
configureData(_data: any) {
}
captureEvent(_data: any) {
}
reportError(error: any, data?: any) {
console.error(error, data);
}
createClientErrorHandler(socketOptions: ClientOptions): ClientErrorHandler {
const handleRecvError = (error: Error, data: string | Uint8Array) => {
if (error.message) {
let method: string | undefined;
disable() {
}
configureUser(_person: Person) {
}
configureData(_data: any) {
}
captureEvent(_data: any) {
}
reportError(error: any, data?: any) {
console.error(error, data);
}
createClientErrorHandler(socketOptions: ClientOptions): ClientErrorHandler {
const handleRecvError = (error: Error, data: string | Uint8Array) => {
if (error.message) {
let method: string | undefined;
if (data instanceof Uint8Array) {
const bytes: number[] = [];
const length = Math.min(data.length, 200);
if (data instanceof Uint8Array) {
const bytes: number[] = [];
const length = Math.min(data.length, 200);
for (let i = 0; i < length; i++) {
bytes.push(data[i]);
}
for (let i = 0; i < length; i++) {
bytes.push(data[i]);
}
const trail = length < data.length ? '...' : '';
const trail = length < data.length ? '...' : '';
if (data.length > 0) {
const item = socketOptions.client[data[0]] as string | [string, any];
method = typeof item === 'string' ? item : item[0];
}
if (data.length > 0) {
const item = socketOptions.client[data[0]] as string | [string, any];
method = typeof item === 'string' ? item : item[0];
}
data = `<${bytes.toString()}${trail}>`;
}
data = `<${bytes.toString()}${trail}>`;
}
this.reportError(error, { data, method });
}
};
this.reportError(error, { data, method });
}
};
return { handleRecvError };
}
return { handleRecvError };
}
}
+27 -27
View File
@@ -1,38 +1,38 @@
import { Injectable, NgZone } from '@angular/core';
export interface FrameLoop {
init(): void;
destroy(): void;
init(): void;
destroy(): void;
}
@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class FrameService {
constructor(private zone: NgZone) {
}
create(frame: (delta: number) => void) {
const zone = this.zone;
let ref = 0;
let last = 0;
constructor(private zone: NgZone) {
}
create(frame: (delta: number) => void) {
const zone = this.zone;
let ref = 0;
let last = 0;
function tick(now: number) {
ref = requestAnimationFrame(tick);
frame((now - last) / 1000);
last = now;
}
function tick(now: number) {
ref = requestAnimationFrame(tick);
frame((now - last) / 1000);
last = now;
}
return {
init() {
if (!ref) {
last = performance.now();
zone.runOutsideAngular(() => ref = requestAnimationFrame(tick));
}
},
destroy() {
cancelAnimationFrame(ref);
ref = 0;
}
};
}
return {
init() {
if (!ref) {
last = performance.now();
zone.runOutsideAngular(() => ref = requestAnimationFrame(tick));
}
},
destroy() {
cancelAnimationFrame(ref);
ref = 0;
}
};
}
}
+257 -257
View File
@@ -17,296 +17,296 @@ import { StorageService } from './storageService';
export interface ClientSocketService extends SocketService<ClientActions, IServerActions> { }
function createSocket(
gameService: GameService, game: PonyTownGame, model: Model, zone: NgZone, options: ClientOptions,
token: string, errorHandler: ClientErrorHandler
gameService: GameService, game: PonyTownGame, model: Model, zone: NgZone, options: ClientOptions,
token: string, errorHandler: ClientErrorHandler
): ClientSocketService {
const socket = createClientSocket<ClientActions, IServerActions>(options, token, errorHandler);
socket.client = new ClientActions(gameService, game, model, zone);
const socket = createClientSocket<ClientActions, IServerActions>(options, token, errorHandler);
socket.client = new ClientActions(gameService, game, model, zone);
if (!socket.supportsBinary) {
throw new Error(BROWSER_NOT_SUPPORTED_ERROR);
}
if (!socket.supportsBinary) {
throw new Error(BROWSER_NOT_SUPPORTED_ERROR);
}
return socket;
return socket;
}
@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class GameService {
playing = false;
joining = false;
offline = false;
protectionError = false;
rateLimitError = false;
versionError = false;
version?: string;
server?: ServerInfo;
servers: ServerInfo[] = [];
error?: string;
leftMessage?: string;
private safelyLeft = false;
private gameLoop?: GameLoop;
private disconnectedTimeout?: any;
private initialized = false;
private update?: boolean;
private locked = false;
constructor(
private model: Model,
private game: PonyTownGame,
private zone: NgZone,
private errorHandler: ErrorHandler,
private errorReporter: ErrorReporter,
private storage: StorageService,
) {
this.pollStatus();
}
get selected() {
return this.game.selected;
}
get account() {
return this.model.account;
}
get canPlay(): boolean {
return !!this.model.pony &&
!!this.model.pony.name &&
!this.model.pending &&
!this.joining &&
!!this.server &&
!this.server.offline &&
!this.rateLimitError &&
!this.versionError &&
!this.locked;
}
get updateWarning(): boolean {
return !!this.update;
}
get filterSwearWords(): boolean {
return !!(this.server && this.server.filter)
|| !!(this.account && this.account.settings && this.account.settings.filterSwearWords);
}
get wasPlaying() {
return this.storage.getBoolean('playing');
}
join(ponyId: string) {
this.errorReporter.captureEvent({ name: 'Join' });
const server = this.server;
playing = false;
joining = false;
offline = false;
protectionError = false;
rateLimitError = false;
versionError = false;
version?: string;
server?: ServerInfo;
servers: ServerInfo[] = [];
error?: string;
leftMessage?: string;
private safelyLeft = false;
private gameLoop?: GameLoop;
private disconnectedTimeout?: any;
private initialized = false;
private update?: boolean;
private locked = false;
constructor(
private model: Model,
private game: PonyTownGame,
private zone: NgZone,
private errorHandler: ErrorHandler,
private errorReporter: ErrorReporter,
private storage: StorageService,
) {
this.pollStatus();
}
get selected() {
return this.game.selected;
}
get account() {
return this.model.account;
}
get canPlay(): boolean {
return !!this.model.pony &&
!!this.model.pony.name &&
!this.model.pending &&
!this.joining &&
!!this.server &&
!this.server.offline &&
!this.rateLimitError &&
!this.versionError &&
!this.locked;
}
get updateWarning(): boolean {
return !!this.update;
}
get filterSwearWords(): boolean {
return !!(this.server && this.server.filter)
|| !!(this.account && this.account.settings && this.account.settings.filterSwearWords);
}
get wasPlaying() {
return this.storage.getBoolean('playing');
}
join(ponyId: string) {
this.errorReporter.captureEvent({ name: 'Join' });
const server = this.server;
if (this.playing || this.joining || !server) {
return Promise.resolve();
}
if (this.playing || this.joining || !server) {
return Promise.resolve();
}
if (typeof WebSocket === 'undefined' || typeof Float32Array === 'undefined') {
return Promise.reject(new Error(BROWSER_NOT_SUPPORTED_ERROR));
}
if (typeof WebSocket === 'undefined' || typeof Float32Array === 'undefined') {
return Promise.reject(new Error(BROWSER_NOT_SUPPORTED_ERROR));
}
this.joining = true;
this.leftMessage = undefined;
this.safelyLeft = false;
this.joining = true;
this.leftMessage = undefined;
this.safelyLeft = false;
return this.model.join(server.id, ponyId)
.then(({ token, alert }) => {
if (!this.joining) {
return false;
}
return this.model.join(server.id, ponyId)
.then(({ token, alert }) => {
if (!this.joining) {
return false;
}
if (!token) {
this.model.accountAlert = alert;
this.joining = false;
return false;
}
if (!token) {
this.model.accountAlert = alert;
this.joining = false;
return false;
}
return this.zone.runOutsideAngular(() => {
const options = { ...socketOptions(), path: server.path, host: server.host };
const errorHandler = this.errorReporter.createClientErrorHandler(options);
const socket = createSocket(this, this.game, this.model, this.zone, options, token, errorHandler);
return this.zone.runOutsideAngular(() => {
const options = { ...socketOptions(), path: server.path, host: server.host };
const errorHandler = this.errorReporter.createClientErrorHandler(options);
const socket = createSocket(this, this.game, this.model, this.zone, options, token, errorHandler);
if (this.gameLoop) {
this.gameLoop.cancel();
}
if (this.gameLoop) {
this.gameLoop.cancel();
}
this.game.startup(socket, this.model.isMod);
this.gameLoop = startGameLoop(this.game, e => this.handleGameError(e));
this.game.startup(socket, this.model.isMod);
this.gameLoop = startGameLoop(this.game, e => this.handleGameError(e));
return this.gameLoop.started
.then(() => {
this.errorReporter.captureEvent({ name: 'gameLoop.started' });
const socketConnected = this.pollUntilConnected(socket);
socket.connect();
return socketConnected;
})
.then(() => {
this.errorReporter.captureEvent({ name: 'socketConnected' });
return true;
})
.catch(e => {
this.errorReporter.captureEvent({ name: 'socket.disconnect()', error: e.message });
socket.disconnect();
throw e;
});
});
})
.then(joined => {
this.errorReporter.captureEvent({ name: joined ? 'Joined game' : 'Not joined game' });
})
.catch((e: RequestError) => {
this.errorReporter.captureEvent({ name: 'Failed to join game', error: e.message });
return this.gameLoop.started
.then(() => {
this.errorReporter.captureEvent({ name: 'gameLoop.started' });
const socketConnected = this.pollUntilConnected(socket);
socket.connect();
return socketConnected;
})
.then(() => {
this.errorReporter.captureEvent({ name: 'socketConnected' });
return true;
})
.catch(e => {
this.errorReporter.captureEvent({ name: 'socket.disconnect()', error: e.message });
socket.disconnect();
throw e;
});
});
})
.then(joined => {
this.errorReporter.captureEvent({ name: joined ? 'Joined game' : 'Not joined game' });
})
.catch((e: RequestError) => {
this.errorReporter.captureEvent({ name: 'Failed to join game', error: e.message });
// if (e.status && e.status > 500 && e.status < 500) {
// this.rateLimitError = true;
// setTimeout(() => this.rateLimitError = false, 5000);
// }
// if (e.status && e.status > 500 && e.status < 500) {
// this.rateLimitError = true;
// setTimeout(() => this.rateLimitError = false, 5000);
// }
this.zone.run(() => this.left('join.catch'));
throw e;
});
}
leave(reason: string) {
this.errorReporter.captureEvent({ name: 'Leave', reason });
this.game.leave();
this.left('leave');
}
joined() {
this.errorReporter.captureEvent({ name: 'Joined' });
this.storage.setBoolean('playing', true);
clearTimeout(this.disconnectedTimeout);
setTimeout(() => {
this.joining = false;
this.playing = true;
});
}
left(from: string, reason = LeaveReason.None) {
this.errorReporter.captureEvent({ name: 'Left', from, reason });
this.storage.setBoolean('playing', false);
this.safelyLeft = true;
this.zone.run(() => this.left('join.catch'));
throw e;
});
}
leave(reason: string) {
this.errorReporter.captureEvent({ name: 'Leave', reason });
this.game.leave();
this.left('leave');
}
joined() {
this.errorReporter.captureEvent({ name: 'Joined' });
this.storage.setBoolean('playing', true);
clearTimeout(this.disconnectedTimeout);
setTimeout(() => {
this.joining = false;
this.playing = true;
});
}
left(from: string, reason = LeaveReason.None) {
this.errorReporter.captureEvent({ name: 'Left', from, reason });
this.storage.setBoolean('playing', false);
this.safelyLeft = true;
if (reason === LeaveReason.Swearing) {
this.leftMessage = 'Kicked for swearing or inappropriate language';
this.locked = true;
} else {
this.leftMessage = undefined;
}
if (reason === LeaveReason.Swearing) {
this.leftMessage = 'Kicked for swearing or inappropriate language';
this.locked = true;
} else {
this.leftMessage = undefined;
}
if (this.gameLoop) {
this.errorReporter.captureEvent({ name: 'gameLoop.cancel()' });
this.gameLoop.cancel();
this.gameLoop = undefined;
}
if (this.gameLoop) {
this.errorReporter.captureEvent({ name: 'gameLoop.cancel()' });
this.gameLoop.cancel();
this.gameLoop = undefined;
}
clearTimeout(this.disconnectedTimeout);
clearTimeout(this.disconnectedTimeout);
setTimeout(() => {
this.joining = false;
this.playing = false;
});
setTimeout(() => {
this.joining = false;
this.playing = false;
});
if (this.locked) {
setTimeout(() => {
this.locked = false;
}, 7000);
}
if (this.locked) {
setTimeout(() => {
this.locked = false;
}, 7000);
}
if (this.model.friends) {
for (const friend of this.model.friends) {
friend.online = false;
friend.entityId = 0;
}
}
if (this.model.friends) {
for (const friend of this.model.friends) {
friend.online = false;
friend.entityId = 0;
}
}
this.game.release();
this.game.onLeft.next();
}
disconnected() {
this.errorReporter.captureEvent({ name: 'Disconnected' });
clearTimeout(this.disconnectedTimeout);
this.game.release();
this.game.onLeft.next();
}
disconnected() {
this.errorReporter.captureEvent({ name: 'Disconnected' });
clearTimeout(this.disconnectedTimeout);
if (!this.safelyLeft) {
this.disconnectedTimeout = setTimeout(() => this.left('disconnected.timeout'), 10000);
}
}
private pollStatus() {
return this.getAndUpdateStatus(this.account)
.finally(() => {
setTimeout(() => this.pollStatus(), this.initialized ? 10000 : 500);
});
}
private getAndUpdateStatus(account: AccountData | undefined) {
if (this.joining || this.playing || !account || !isFocused()) {
return Promise.resolve();
} else {
return this.model.status(this.initialized)
.then(status => this.updateStatus(account, status))
.catch((e: RequestError) => {
DEVELOPMENT && console.error(e);
this.offline = e.message === OFFLINE_ERROR;
this.versionError = e.message === VERSION_ERROR;
this.protectionError = e.message === PROTECTION_ERROR;
});
}
}
private updateStatus(account: AccountData, status: GameStatus) {
this.initialized = true;
this.offline = false;
this.version = status.version;
this.update = status.update;
if (!this.safelyLeft) {
this.disconnectedTimeout = setTimeout(() => this.left('disconnected.timeout'), 10000);
}
}
private pollStatus() {
return this.getAndUpdateStatus(this.account)
.finally(() => {
setTimeout(() => this.pollStatus(), this.initialized ? 10000 : 500);
});
}
private getAndUpdateStatus(account: AccountData | undefined) {
if (this.joining || this.playing || !account || !isFocused()) {
return Promise.resolve();
} else {
return this.model.status(this.initialized)
.then(status => this.updateStatus(account, status))
.catch((e: RequestError) => {
DEVELOPMENT && console.error(e);
this.offline = e.message === OFFLINE_ERROR;
this.versionError = e.message === VERSION_ERROR;
this.protectionError = e.message === PROTECTION_ERROR;
});
}
}
private updateStatus(account: AccountData, status: GameStatus) {
this.initialized = true;
this.offline = false;
this.version = status.version;
this.update = status.update;
for (const server of status.servers) {
const existing = findById(this.servers, server.id);
for (const server of status.servers) {
const existing = findById(this.servers, server.id);
if (existing) {
merge(existing, server);
} else if ('name' in server) {
const info = server as ServerInfo;
info.countryFlags = info.flag && /^[a-z]{2}( [a-z]{2})*$/.test(info.flag) ? info.flag.split(/ /g) : [];
if (existing) {
merge(existing, server);
} else if ('name' in server) {
const info = server as ServerInfo;
info.countryFlags = info.flag && /^[a-z]{2}( [a-z]{2})*$/.test(info.flag) ? info.flag.split(/ /g) : [];
if (info.name && account && meetsRequirement(account, info.require)) {
this.servers.push(info);
}
} else {
// got new server on the list
this.initialized = false;
}
}
if (info.name && account && meetsRequirement(account, info.require)) {
this.servers.push(info);
}
} else {
// got new server on the list
this.initialized = false;
}
}
for (let i = this.servers.length - 1; i >= 0; i--) {
if (!findById(status.servers, this.servers[i].id)) {
this.servers.splice(i, 1);
}
}
for (let i = this.servers.length - 1; i >= 0; i--) {
if (!findById(status.servers, this.servers[i].id)) {
this.servers.splice(i, 1);
}
}
if (isLanguage('ru')) {
this.servers.sort(sortServersForRussian);
}
if (isLanguage('ru')) {
this.servers.sort(sortServersForRussian);
}
if (!this.server && account.settings.defaultServer) {
this.server = findById(this.servers, account.settings.defaultServer);
if (!this.server && account.settings.defaultServer) {
this.server = findById(this.servers, account.settings.defaultServer);
if (DEVELOPMENT && /join/.test(this.model.pony.name)) {
setTimeout(() => this.join(this.model.pony.id));
}
}
if (DEVELOPMENT && /join/.test(this.model.pony.name)) {
setTimeout(() => this.join(this.model.pony.id));
}
}
if (!includes(this.servers, this.server)) {
this.server = undefined;
}
}
private handleGameError(error: Error) {
this.errorReporter.captureEvent({ name: 'handleGameError', error: error.message });
this.error = error.message;
this.errorHandler.handleError(error);
this.leave('handleGameError');
}
private pollUntilConnected(socket: ClientSocketService) {
return new Promise<void>((resolve, reject) => {
const interval = setInterval(() => {
if (socket.isConnected) {
clearInterval(interval);
this.zone.run(resolve);
} else if (!this.joining) {
clearInterval(interval);
this.zone.run(() => reject(new Error('Cancelled (poll)')));
}
}, 10);
});
}
if (!includes(this.servers, this.server)) {
this.server = undefined;
}
}
private handleGameError(error: Error) {
this.errorReporter.captureEvent({ name: 'handleGameError', error: error.message });
this.error = error.message;
this.errorHandler.handleError(error);
this.leave('handleGameError');
}
private pollUntilConnected(socket: ClientSocketService) {
return new Promise<void>((resolve, reject) => {
const interval = setInterval(() => {
if (socket.isConnected) {
clearInterval(interval);
this.zone.run(resolve);
} else if (!this.joining) {
clearInterval(interval);
this.zone.run(() => reject(new Error('Cancelled (poll)')));
}
}, 10);
});
}
}
+29 -29
View File
@@ -2,40 +2,40 @@ import { Injectable } from '@angular/core';
import { StorageService } from './storageService';
interface InstallEvent extends Event {
prompt(): void;
userChoice: Promise<'accepted' | 'dismissed'>;
prompt(): void;
userChoice: Promise<'accepted' | 'dismissed'>;
}
@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class InstallService {
private installEvent?: InstallEvent;
constructor(private storage: StorageService) {
if (!this.storage.getBoolean('install-dismissed')) {
window.addEventListener('beforeinstallprompt', event => {
event.preventDefault();
this.installEvent = event as any;
});
}
}
get canInstall() {
return !!this.installEvent || (DEVELOPMENT && localStorage.getItem('install'));
}
install() {
if (!this.installEvent) {
return Promise.reject(new Error('Cannot install'));
}
private installEvent?: InstallEvent;
constructor(private storage: StorageService) {
if (!this.storage.getBoolean('install-dismissed')) {
window.addEventListener('beforeinstallprompt', event => {
event.preventDefault();
this.installEvent = event as any;
});
}
}
get canInstall() {
return !!this.installEvent || (DEVELOPMENT && localStorage.getItem('install'));
}
install() {
if (!this.installEvent) {
return Promise.reject(new Error('Cannot install'));
}
this.installEvent.prompt();
this.installEvent.prompt();
return this.installEvent.userChoice
.finally(() => {
this.installEvent = undefined;
});
}
dismiss() {
this.installEvent = undefined;
this.storage.setBoolean('install-dismissed', true);
}
return this.installEvent.userChoice
.finally(() => {
this.installEvent = undefined;
});
}
dismiss() {
this.installEvent = undefined;
this.storage.setBoolean('install-dismissed', true);
}
}
@@ -2,43 +2,43 @@ import { Injectable, NgZone } from '@angular/core';
import { removeItem } from '../../common/utils';
@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class IntervalUpdateService {
private interval: any;
private actions: (() => void)[] = [];
constructor(private zone: NgZone) {
}
subscribe(action: () => void) {
this.actions.push(action);
private interval: any;
private actions: (() => void)[] = [];
constructor(private zone: NgZone) {
}
subscribe(action: () => void) {
this.actions.push(action);
if (!this.interval) {
this.zone.runOutsideAngular(() => {
this.interval = setInterval(() => {
this.actions.forEach(a => a());
}, 1000 * 10);
});
}
if (!this.interval) {
this.zone.runOutsideAngular(() => {
this.interval = setInterval(() => {
this.actions.forEach(a => a());
}, 1000 * 10);
});
}
return () => {
removeItem(this.actions, action);
return () => {
removeItem(this.actions, action);
if (this.actions.length === 0) {
clearInterval(this.interval);
this.interval = undefined;
}
};
}
toggle(action: () => void) {
let unsubscribe: (() => void) | undefined;
if (this.actions.length === 0) {
clearInterval(this.interval);
this.interval = undefined;
}
};
}
toggle(action: () => void) {
let unsubscribe: (() => void) | undefined;
return (on: boolean) => {
if (on && !unsubscribe) {
unsubscribe = this.subscribe(action);
} else if (!on && unsubscribe) {
unsubscribe();
unsubscribe = undefined;
}
};
}
return (on: boolean) => {
if (on && !unsubscribe) {
unsubscribe = this.subscribe(action);
} else if (!on && unsubscribe) {
unsubscribe();
unsubscribe = undefined;
}
};
}
}
+119 -119
View File
@@ -4,142 +4,142 @@ import { Document, LiveResponse, IAdminServerActions, BaseValues } from '../../c
import { ClientAdminActions } from '../../client/clientAdminActions';
export interface Options<T> {
// collection
beforeUpdate?: (updates: T[]) => void;
onUpdated?: (added: T[], all: T[]) => void;
onFinished?: () => void;
// item
decode: (fields: any[], base: BaseValues) => T;
onUpdate?: (oldItem: T, newItem: T) => void;
onDelete?: (item: T) => void;
deleteItems?: boolean;
ignore?: (item: T) => boolean;
// collection
beforeUpdate?: (updates: T[]) => void;
onUpdated?: (added: T[], all: T[]) => void;
onFinished?: () => void;
// item
decode: (fields: any[], base: BaseValues) => T;
onUpdate?: (oldItem: T, newItem: T) => void;
onDelete?: (item: T) => void;
deleteItems?: boolean;
ignore?: (item: T) => boolean;
}
export class LiveCollection<T extends Document> {
items: T[] = [];
finished = false;
private running = true;
private itemsMap = new Map<string, T>();
private liveTimeout: any;
constructor(
private name: 'events',
private rate: number,
private getKey: (item: T) => string,
private options: Options<T>,
private socket: SocketService<ClientAdminActions, IAdminServerActions>,
private timestamp = (new Date(0)).toISOString(),
private logError = (e: Error) => console.error(e.stack),
) {
}
get(key: string) {
return this.itemsMap.get(key);
}
push(item: T) {
this.items.push(item);
this.itemsMap.set(this.getKey(item), item);
return item;
}
remove(key: string) {
return this.server.removeItem(this.name, key)
.then(() => this.removeItem(key, true, true));
}
removeItem(key: string, deleted = false, removeFromList = false) {
const item = this.itemsMap.get(key);
items: T[] = [];
finished = false;
private running = true;
private itemsMap = new Map<string, T>();
private liveTimeout: any;
constructor(
private name: 'events',
private rate: number,
private getKey: (item: T) => string,
private options: Options<T>,
private socket: SocketService<ClientAdminActions, IAdminServerActions>,
private timestamp = (new Date(0)).toISOString(),
private logError = (e: Error) => console.error(e.stack),
) {
}
get(key: string) {
return this.itemsMap.get(key);
}
push(item: T) {
this.items.push(item);
this.itemsMap.set(this.getKey(item), item);
return item;
}
remove(key: string) {
return this.server.removeItem(this.name, key)
.then(() => this.removeItem(key, true, true));
}
removeItem(key: string, deleted = false, removeFromList = false) {
const item = this.itemsMap.get(key);
if (item) {
if (removeFromList || this.options.deleteItems) {
removeItem(this.items, item);
this.itemsMap.delete(key);
} else if (deleted) {
item.deleted = true;
}
if (item) {
if (removeFromList || this.options.deleteItems) {
removeItem(this.items, item);
this.itemsMap.delete(key);
} else if (deleted) {
item.deleted = true;
}
if (deleted && this.options.onDelete) {
this.options.onDelete(item);
}
}
}
assignAccount(id: string, account: string) {
return this.server.assignAccount(this.name, id, account);
}
live(): Promise<void> {
if (!this.running)
return Promise.resolve();
if (deleted && this.options.onDelete) {
this.options.onDelete(item);
}
}
}
assignAccount(id: string, account: string) {
return this.server.assignAccount(this.name, id, account);
}
live(): Promise<void> {
if (!this.running)
return Promise.resolve();
clearTimeout(this.liveTimeout);
clearTimeout(this.liveTimeout);
return this.update()
.catch(this.logError)
.then(more => {
this.liveTimeout = setTimeout(() => this.live(), more ? 100 : this.rate);
});
}
stop() {
this.running = false;
}
read({ updates, deletes, base, more }: LiveResponse, liveFetch = true) {
const items = updates.map(i => this.options.decode(i, base));
return this.update()
.catch(this.logError)
.then(more => {
this.liveTimeout = setTimeout(() => this.live(), more ? 100 : this.rate);
});
}
stop() {
this.running = false;
}
read({ updates, deletes, base, more }: LiveResponse, liveFetch = true) {
const items = updates.map(i => this.options.decode(i, base));
if (liveFetch) {
const timestamp = items
.reduce((max, i) => max.getTime() < i.updatedAt.getTime() ? i.updatedAt : max, new Date(this.timestamp));
this.timestamp = timestamp.toISOString();
}
if (liveFetch) {
const timestamp = items
.reduce((max, i) => max.getTime() < i.updatedAt.getTime() ? i.updatedAt : max, new Date(this.timestamp));
this.timestamp = timestamp.toISOString();
}
if (this.options.beforeUpdate) {
this.options.beforeUpdate(items);
}
if (this.options.beforeUpdate) {
this.options.beforeUpdate(items);
}
const { added, all } = this.applyUpdates(items, liveFetch);
const { added, all } = this.applyUpdates(items, liveFetch);
if (this.options.onUpdated && items.length) {
this.options.onUpdated(added, all);
}
if (this.options.onUpdated && items.length) {
this.options.onUpdated(added, all);
}
deletes.forEach(key => this.removeItem(key, true));
deletes.forEach(key => this.removeItem(key, true));
if (liveFetch) {
const finished = this.finished || !more;
if (liveFetch) {
const finished = this.finished || !more;
if (!this.finished && finished) {
this.finished = true;
if (!this.finished && finished) {
this.finished = true;
if (this.options.onFinished) {
this.options.onFinished();
}
}
}
if (this.options.onFinished) {
this.options.onFinished();
}
}
}
return more;
}
private get server() {
return this.socket.server;
}
private update() {
return this.socket.isConnected ? this.server.getAll(this.name, this.timestamp).then(r => this.read(r)) : Promise.resolve(false);
}
private applyUpdates(updates: T[], liveFetch: boolean) {
const added: T[] = [];
const all: T[] = [];
return more;
}
private get server() {
return this.socket.server;
}
private update() {
return this.socket.isConnected ? this.server.getAll(this.name, this.timestamp).then(r => this.read(r)) : Promise.resolve(false);
}
private applyUpdates(updates: T[], liveFetch: boolean) {
const added: T[] = [];
const all: T[] = [];
updates.forEach(update => {
const doc = this.get(this.getKey(update));
updates.forEach(update => {
const doc = this.get(this.getKey(update));
if (doc) {
if (this.options.onUpdate) {
this.options.onUpdate(doc, update);
} else {
Object.assign(doc, update);
}
all.push(doc);
} else if (!liveFetch || !this.options.ignore || !this.options.ignore(update)) {
this.push(update);
added.push(update);
all.push(update);
}
});
if (doc) {
if (this.options.onUpdate) {
this.options.onUpdate(doc, update);
} else {
Object.assign(doc, update);
}
all.push(doc);
} else if (!liveFetch || !this.options.ignore || !this.options.ignore(update)) {
this.push(update);
added.push(update);
all.push(update);
}
});
return { added, all };
}
return { added, all };
}
}
+385 -385
View File
@@ -5,18 +5,18 @@ import { merge } from 'lodash';
import { Subject } from 'rxjs';
import { HASH } from '../../generated/hash';
import {
AccountData, UpdateAccountData, AccountSettings, GameStatus, SocialSiteInfo, PonyObject, JoinResponse,
OAuthProvider, EntitiesEditorInfo, FriendData, PalettePonyInfo, HiddenPlayer
AccountData, UpdateAccountData, AccountSettings, GameStatus, SocialSiteInfo, PonyObject, JoinResponse,
OAuthProvider, EntitiesEditorInfo, FriendData, PalettePonyInfo, HiddenPlayer
} from '../../common/interfaces';
import { createDefaultPony, syncLockedPonyInfo, mockPaletteManager } from '../../common/ponyInfo';
import { removeById, observableToPromise, delay, computeFriendsCRC } from '../../common/utils';
import { isMod, getSupporterInviteLimit, getCharacterLimit } from '../../common/accountUtils';
import {
NAME_ERROR, ACCESS_ERROR, CHARACTER_SAVING_ERROR, NOT_AUTHENTICATED_ERROR, OFFLINE_ERROR, PROTECTION_ERROR
NAME_ERROR, ACCESS_ERROR, CHARACTER_SAVING_ERROR, NOT_AUTHENTICATED_ERROR, OFFLINE_ERROR, PROTECTION_ERROR
} from '../../common/errors';
import { version, host } from '../../client/data';
import {
toSocialSiteInfo, cleanName, validatePonyName, isStandalone, attachDebugMethod
toSocialSiteInfo, cleanName, validatePonyName, isStandalone, attachDebugMethod
} from '../../client/clientUtils';
import { ErrorReporter } from './errorReporter';
import { randomString } from '../../common/stringUtils';
@@ -26,463 +26,463 @@ import { SECOND, PLAYER_DESC_MAX_LENGTH } from '../../common/constants';
import { canUseTag } from '../../common/tags';
export interface Friend extends FriendData {
entityId: number;
crc: number;
online: boolean;
ponyInfo: PalettePonyInfo | undefined;
actualName: string;
entityId: number;
crc: number;
online: boolean;
ponyInfo: PalettePonyInfo | undefined;
actualName: string;
}
const LIMIT_ERROR = 'Request limit reached, please wait';
const noneSite: SocialSiteInfo = { id: '', name: 'none', url: '', icon: '', color: '#222' };
const modStatus = {
mod: false,
check: {} as any,
editor: {
names: [],
typeToName: [],
nameToTypes: [],
} as EntitiesEditorInfo,
mod: false,
check: {} as any,
editor: {
names: [],
typeToName: [],
nameToTypes: [],
} as EntitiesEditorInfo,
};
function compareStrings(a: string | undefined, b: string | undefined) {
return (a || '').localeCompare(b || '');
return (a || '').localeCompare(b || '');
}
function comparePonies(a: PonyObject, b: PonyObject) {
return compareStrings(a.name, b.name) || compareStrings(a.id, b.id);
return compareStrings(a.name, b.name) || compareStrings(a.id, b.id);
}
function getDefaultPony(ponies: PonyObject[]) {
let result = ponies[0];
let result = ponies[0];
for (let i = 1; i < ponies.length; i++) {
if (compareStrings(result.lastUsed, ponies[i].lastUsed) < 0) {
result = ponies[i];
}
}
for (let i = 1; i < ponies.length; i++) {
if (compareStrings(result.lastUsed, ponies[i].lastUsed) < 0) {
result = ponies[i];
}
}
return result || createDefaultPonyObject();
return result || createDefaultPonyObject();
}
export function createDefaultPonyObject(): PonyObject {
return {
id: '',
name: '',
info: '',
ponyInfo: createDefaultPony(),
};
return {
id: '',
name: '',
info: '',
ponyInfo: createDefaultPony(),
};
}
export function getPonyTag(pony: PonyObject, account: AccountData | undefined) {
if (account) {
const tag = canUseTag(account, pony.tag || '') ? pony.tag : undefined;
return (!tag && account.supporter && !pony.hideSupport) ? `sup${account.supporter}` : tag;
} else {
return undefined;
}
if (account) {
const tag = canUseTag(account, pony.tag || '') ? pony.tag : undefined;
return (!tag && account.supporter && !pony.hideSupport) ? `sup${account.supporter}` : tag;
} else {
return undefined;
}
}
const entityTypeToName = new Map<number, string>();
const entityNameToTypes = new Map<string, number[]>();
export function getEntityNames() {
return modStatus.editor.names;
return modStatus.editor.names;
}
export function getEntityTypesFromName(name: string) {
return entityNameToTypes.get(name);
return entityNameToTypes.get(name);
}
export function getEntityNameFromType(type: number) {
return entityTypeToName.get(type);
return entityTypeToName.get(type);
}
export function compareFriends(a: Friend, b: Friend) {
return a.online !== b.online ? (a.online ? -1 : 1) : a.accountName.localeCompare(b.accountName);
return a.online !== b.online ? (a.online ? -1 : 1) : a.accountName.localeCompare(b.accountName);
}
@Injectable({ providedIn: 'root' })
export class Model {
loading = true;
loadingError?: string;
account?: AccountData;
ponies: PonyObject[] = [];
pending = false;
sites: SocialSiteInfo[] = [noneSite];
accountPromise!: Promise<AccountData | undefined>;
accountChanged = new Subject<void>();
protectionErrors = new Subject<void>();
authError?: string;
accountAlert?: string;
mergedAccount = false;
updating = false;
updatingTakesLongTime = false;
suffix = '';
friends: Friend[] | undefined = undefined;
private _pony: PonyObject = createDefaultPonyObject();
constructor(
private http: HttpClient,
private router: Router,
private storage: StorageService,
private errorReporter: ErrorReporter,
) {
this.initialize();
loading = true;
loadingError?: string;
account?: AccountData;
ponies: PonyObject[] = [];
pending = false;
sites: SocialSiteInfo[] = [noneSite];
accountPromise!: Promise<AccountData | undefined>;
accountChanged = new Subject<void>();
protectionErrors = new Subject<void>();
authError?: string;
accountAlert?: string;
mergedAccount = false;
updating = false;
updatingTakesLongTime = false;
suffix = '';
friends: Friend[] | undefined = undefined;
private _pony: PonyObject = createDefaultPonyObject();
constructor(
private http: HttpClient,
private router: Router,
private storage: StorageService,
private errorReporter: ErrorReporter,
) {
this.initialize();
// handle completed sign-in
if (typeof window !== 'undefined') {
window.addEventListener('message', event => {
if (event.data && event.data.type === 'loaded-page') {
const path = event.data.path;
// handle completed sign-in
if (typeof window !== 'undefined') {
window.addEventListener('message', event => {
if (event.data && event.data.type === 'loaded-page') {
const path = event.data.path;
if (event.source && 'close' in event.source) {
event.source.close();
}
if (event.source && 'close' in event.source) {
event.source.close();
}
this.initialize();
this.accountPromise.then(() => router.navigateByUrl(path));
}
});
}
this.initialize();
this.accountPromise.then(() => router.navigateByUrl(path));
}
});
}
if (DEVELOPMENT) {
attachDebugMethod('ddos', () => this.protectionErrors.next());
attachDebugMethod('userModel', this);
}
}
private initialize() {
this.loading = true;
this.account = undefined;
this.loadingError = undefined;
this.accountAlert = undefined;
this.ponies = [];
this.friends = undefined;
this.sites = [noneSite];
this._pony = createDefaultPonyObject();
this.storage.setItem('bid', this.storage.getItem('bid') || randomString(20));
this.accountPromise = this.initializeAccount();
}
private initializeAccount(): Promise<AccountData | undefined> {
return this.getAccount()
.then(account => {
if (!account) {
throw new Error(ACCESS_ERROR);
}
if (DEVELOPMENT) {
attachDebugMethod('ddos', () => this.protectionErrors.next());
attachDebugMethod('userModel', this);
}
}
private initialize() {
this.loading = true;
this.account = undefined;
this.loadingError = undefined;
this.accountAlert = undefined;
this.ponies = [];
this.friends = undefined;
this.sites = [noneSite];
this._pony = createDefaultPonyObject();
this.storage.setItem('bid', this.storage.getItem('bid') || randomString(20));
this.accountPromise = this.initializeAccount();
}
private initializeAccount(): Promise<AccountData | undefined> {
return this.getAccount()
.then(account => {
if (!account) {
throw new Error(ACCESS_ERROR);
}
if ('limit' in account) {
throw new Error(LIMIT_ERROR);
}
if ('limit' in account) {
throw new Error(LIMIT_ERROR);
}
this.errorReporter.configureUser({ id: account.id, username: account.name });
this.errorReporter.configureUser({ id: account.id, username: account.name });
try {
modStatus.mod = isMod(account);
modStatus.check = account.check;
modStatus.editor = account.editor || modStatus.editor;
} catch { }
try {
modStatus.mod = isMod(account);
modStatus.check = account.check;
modStatus.editor = account.editor || modStatus.editor;
} catch { }
if (modStatus.editor) {
modStatus.editor.typeToName.forEach(({ type, name }) => entityTypeToName.set(type, name));
modStatus.editor.nameToTypes.forEach(({ types, name }) => entityNameToTypes.set(name, types));
}
if (modStatus.editor) {
modStatus.editor.typeToName.forEach(({ type, name }) => entityTypeToName.set(type, name));
modStatus.editor.nameToTypes.forEach(({ types, name }) => entityNameToTypes.set(name, types));
}
this.account = account;
this.sites = [noneSite, ...(account.sites || []).map(toSocialSiteInfo)];
this.ponies = account.ponies ? account.ponies.sort(comparePonies) : [];
this.friends = undefined;
this.account = account;
this.sites = [noneSite, ...(account.sites || []).map(toSocialSiteInfo)];
this.ponies = account.ponies ? account.ponies.sort(comparePonies) : [];
this.friends = undefined;
this.selectPony(getDefaultPony(this.ponies));
this.storage.setItem('vid', account.id);
this.loading = false;
this.accountAlert = account.alert;
this.accountChanged.next();
this.fetchFriends();
this.selectPony(getDefaultPony(this.ponies));
this.storage.setItem('vid', account.id);
this.loading = false;
this.accountAlert = account.alert;
this.accountChanged.next();
this.fetchFriends();
return account;
})
.catch((e: Error) => {
if (e.message === ACCESS_ERROR) {
this.loading = false;
this.storage.setItem('vid', '---');
} else if (e.message === LIMIT_ERROR) {
this.loadingError = 'request-limit';
return delay(5000).then(() => this.initializeAccount());
} else if (e.message === OFFLINE_ERROR) {
this.loadingError = 'cannot-connect';
return delay(5000).then(() => this.initializeAccount());
} else if (e.message === PROTECTION_ERROR) {
this.loadingError = 'cloudflare-error';
this.protectionErrors.next();
// } else if (e.message === VERSION_ERROR) {
// this.updating = true;
} else {
setTimeout(() => this.loadingError = 'unexpected-error', 5 * SECOND);
console.error(e);
}
return account;
})
.catch((e: Error) => {
if (e.message === ACCESS_ERROR) {
this.loading = false;
this.storage.setItem('vid', '---');
} else if (e.message === LIMIT_ERROR) {
this.loadingError = 'request-limit';
return delay(5000).then(() => this.initializeAccount());
} else if (e.message === OFFLINE_ERROR) {
this.loadingError = 'cannot-connect';
return delay(5000).then(() => this.initializeAccount());
} else if (e.message === PROTECTION_ERROR) {
this.loadingError = 'cloudflare-error';
this.protectionErrors.next();
// } else if (e.message === VERSION_ERROR) {
// this.updating = true;
} else {
setTimeout(() => this.loadingError = 'unexpected-error', 5 * SECOND);
console.error(e);
}
return undefined;
});
}
private fetchFriends() {
this.getFriends()
.then(friends => {
this.friends = friends.map(f => ({
...f,
online: false,
entityId: 0,
crc: 0,
ponyInfo: f.pony && decodePonyInfo(f.pony, mockPaletteManager) || undefined,
actualName: '',
})).sort(compareFriends);
})
.catch(e => {
DEVELOPMENT && console.error(e);
setTimeout(() => this.fetchFriends(), 5000);
});
}
get characterLimit() {
return this.account ? getCharacterLimit(this.account) : 0;
}
get supporterInviteLimit() {
return this.account ? getSupporterInviteLimit(this.account) : 0;
}
get isMod() {
return modStatus.mod;
}
get modCheck() {
return modStatus.check;
}
get editorInfo() {
return modStatus.editor;
}
get pony() {
return this._pony;
}
get supporter() {
return this.account && this.account.supporter || 0;
}
get missingBirthdate() {
return !!this.account && !this.account.birthdate;
}
computeFriendsCRC() {
return this.friends ? computeFriendsCRC(this.friends.map(f => f.accountId)) : 0;
}
parsePonyObject(pony: PonyObject): PonyObject {
try {
const ponyInfo = decompressPonyString(pony.info, true);
return { ponyInfo, ...pony };
} catch (e) {
this.errorReporter.reportError(e, { ponyInfo: pony.info });
this.errorReporter.reportError('Pony info reading error', { originalError: e.message, ponyInfo: pony.info });
throw new Error('Error while reading pony info');
}
}
selectPony(pony: PonyObject) {
const copy = this.parsePonyObject(pony);
copy.ponyInfo && syncLockedPonyInfo(copy.ponyInfo);
this._pony = copy;
}
// account
signIn(provider: OAuthProvider) {
this.authError = undefined;
this.openAuth(provider.url!);
}
connectSite(provider: OAuthProvider) {
this.authError = undefined;
this.openAuth(`${provider.url}/merge`);
}
signOut() {
this.authError = undefined;
return undefined;
});
}
private fetchFriends() {
this.getFriends()
.then(friends => {
this.friends = friends.map(f => ({
...f,
online: false,
entityId: 0,
crc: 0,
ponyInfo: f.pony && decodePonyInfo(f.pony, mockPaletteManager) || undefined,
actualName: '',
})).sort(compareFriends);
})
.catch(e => {
DEVELOPMENT && console.error(e);
setTimeout(() => this.fetchFriends(), 5000);
});
}
get characterLimit() {
return this.account ? getCharacterLimit(this.account) : 0;
}
get supporterInviteLimit() {
return this.account ? getSupporterInviteLimit(this.account) : 0;
}
get isMod() {
return modStatus.mod;
}
get modCheck() {
return modStatus.check;
}
get editorInfo() {
return modStatus.editor;
}
get pony() {
return this._pony;
}
get supporter() {
return this.account && this.account.supporter || 0;
}
get missingBirthdate() {
return !!this.account && !this.account.birthdate;
}
computeFriendsCRC() {
return this.friends ? computeFriendsCRC(this.friends.map(f => f.accountId)) : 0;
}
parsePonyObject(pony: PonyObject): PonyObject {
try {
const ponyInfo = decompressPonyString(pony.info, true);
return { ponyInfo, ...pony };
} catch (e) {
this.errorReporter.reportError(e, { ponyInfo: pony.info });
this.errorReporter.reportError('Pony info reading error', { originalError: e.message, ponyInfo: pony.info });
throw new Error('Error while reading pony info');
}
}
selectPony(pony: PonyObject) {
const copy = this.parsePonyObject(pony);
copy.ponyInfo && syncLockedPonyInfo(copy.ponyInfo);
this._pony = copy;
}
// account
signIn(provider: OAuthProvider) {
this.authError = undefined;
this.openAuth(provider.url!);
}
connectSite(provider: OAuthProvider) {
this.authError = undefined;
this.openAuth(`${provider.url}/merge`);
}
signOut() {
this.authError = undefined;
return this.post<AccountData | undefined>('/auth/sign-out', {}, false)
.catch(e => console.error(e))
.then(() => this.initialize())
.then(() => this.router.navigate(['/']));
}
private openAuth(url: string) {
url = `${host.replace(/\/$/, '')}${url}`;
return this.post<AccountData | undefined>('/auth/sign-out', {}, false)
.catch(e => console.error(e))
.then(() => this.initialize())
.then(() => this.router.navigate(['/']));
}
private openAuth(url: string) {
url = `${host.replace(/\/$/, '')}${url}`;
if (isStandalone()) {
window.open(url);
} else {
location.href = url;
}
}
getAccount() {
return this.post<AccountData | { limit: true; } | undefined>('/api1/account', {}, false);
}
getAccountCharacters() {
return this.post<PonyObject[]>('/api/account-characters', {});
}
updateAccount(account: Partial<UpdateAccountData>) {
return this.post<AccountData>('/api/account-update', { account })
.then(a => merge(this.account, a));
}
saveSettings(settings: AccountSettings) {
return this.post<AccountData>('/api/account-settings', { settings })
.then(a => merge(this.account, a));
}
removeSite(siteId: string) {
return this.post('/api/remove-site', { siteId })
.then(() => {
if (this.account && this.account.sites) {
removeById(this.account.sites, siteId);
}
});
}
unhidePlayer(hideId: string) {
return this.post('/api/remove-hide', { hideId });
}
verifyAccount() {
const verificationId = this.storage.getItem('vid');
const accountId = this.account && this.account.id || '---';
if (isStandalone()) {
window.open(url);
} else {
location.href = url;
}
}
getAccount() {
return this.post<AccountData | { limit: true; } | undefined>('/api1/account', {}, false);
}
getAccountCharacters() {
return this.post<PonyObject[]>('/api/account-characters', {});
}
updateAccount(account: Partial<UpdateAccountData>) {
return this.post<AccountData>('/api/account-update', { account })
.then(a => merge(this.account, a));
}
saveSettings(settings: AccountSettings) {
return this.post<AccountData>('/api/account-settings', { settings })
.then(a => merge(this.account, a));
}
removeSite(siteId: string) {
return this.post('/api/remove-site', { siteId })
.then(() => {
if (this.account && this.account.sites) {
removeById(this.account.sites, siteId);
}
});
}
unhidePlayer(hideId: string) {
return this.post('/api/remove-hide', { hideId });
}
verifyAccount() {
const verificationId = this.storage.getItem('vid');
const accountId = this.account && this.account.id || '---';
if (!this.loading && verificationId && accountId !== verificationId) {
this.initialize();
}
}
getHides(page: number) {
return this.post<HiddenPlayer[]>('/api/get-hides', { page });
}
getFriends() {
return this.post<FriendData[]>('/api/get-friends', {});
}
// ponies
savePony(pony: PonyObject, fast = false) {
return Promise.resolve()
.then(() => {
if (this.pending) {
throw new Error('Saving in progress');
}
if (!this.loading && verificationId && accountId !== verificationId) {
this.initialize();
}
}
getHides(page: number) {
return this.post<HiddenPlayer[]>('/api/get-hides', { page });
}
getFriends() {
return this.post<FriendData[]>('/api/get-friends', {});
}
// ponies
savePony(pony: PonyObject, fast = false) {
return Promise.resolve()
.then(() => {
if (this.pending) {
throw new Error('Saving in progress');
}
pony.name = cleanName(pony.name);
pony.desc = pony.desc && pony.desc.substr(0, PLAYER_DESC_MAX_LENGTH) || '';
pony.name = cleanName(pony.name);
pony.desc = pony.desc && pony.desc.substr(0, PLAYER_DESC_MAX_LENGTH) || '';
if (!validatePonyName(pony.name)) {
throw new Error(NAME_ERROR);
}
if (!validatePonyName(pony.name)) {
throw new Error(NAME_ERROR);
}
if (pony.ponyInfo) {
pony.info = compressPonyString(pony.ponyInfo);
}
if (pony.ponyInfo) {
pony.info = compressPonyString(pony.ponyInfo);
}
const { id, name, desc, site, tag, info, hideSupport, respawnAtSpawn } = pony;
const { id, name, desc, site, tag, info, hideSupport, respawnAtSpawn } = pony;
if (!fast) {
this.pending = true;
}
if (!fast) {
this.pending = true;
}
return this.post<PonyObject | undefined>('/api/pony/save', {
pony: { id, name, desc, site, tag, info, hideSupport, respawnAtSpawn }
});
})
.catch((e: Error) => {
if (e.message === CHARACTER_SAVING_ERROR) {
this.errorReporter.reportError(e, { pony });
}
return this.post<PonyObject | undefined>('/api/pony/save', {
pony: { id, name, desc, site, tag, info, hideSupport, respawnAtSpawn }
});
})
.catch((e: Error) => {
if (e.message === CHARACTER_SAVING_ERROR) {
this.errorReporter.reportError(e, { pony });
}
throw e;
})
.then(newPony => {
if (!newPony) {
throw new Error('Failed to save pony');
}
throw e;
})
.then(newPony => {
if (!newPony) {
throw new Error('Failed to save pony');
}
if (pony.id) {
removeById(this.ponies, pony.id);
} else {
this.account!.characterCount++;
}
if (pony.id) {
removeById(this.ponies, pony.id);
} else {
this.account!.characterCount++;
}
this.ponies.push(newPony);
this.ponies.sort(comparePonies);
this.ponies.push(newPony);
this.ponies.sort(comparePonies);
if (this.pony === pony) {
this.selectPony(newPony);
}
if (this.pony === pony) {
this.selectPony(newPony);
}
return newPony;
})
.finally(() => this.pending = false);
}
removePony(pony: PonyObject) {
return this.post('/api/pony/remove', { id: pony.id })
.then(() => {
removeById(this.ponies, pony.id);
this.account!.characterCount--;
return newPony;
})
.finally(() => this.pending = false);
}
removePony(pony: PonyObject) {
return this.post('/api/pony/remove', { id: pony.id })
.then(() => {
removeById(this.ponies, pony.id);
this.account!.characterCount--;
if (this.pony === pony) {
this.selectPony(getDefaultPony(this.ponies));
}
});
}
loadPonies() {
return this.getAccountCharacters()
.then(ponies => {
if (this.account) {
this.account.ponies = ponies || [];
this.ponies = this.account.ponies.sort(comparePonies);
}
});
}
sortPonies() {
this.ponies.sort(comparePonies);
}
// game
status(short: boolean): Promise<GameStatus> {
let age = 6;
if (this.pony === pony) {
this.selectPony(getDefaultPony(this.ponies));
}
});
}
loadPonies() {
return this.getAccountCharacters()
.then(ponies => {
if (this.account) {
this.account.ponies = ponies || [];
this.ponies = this.account.ponies.sort(comparePonies);
}
});
}
sortPonies() {
this.ponies.sort(comparePonies);
}
// game
status(short: boolean): Promise<GameStatus> {
let age = 6;
if (this.account) {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;
if (this.account) {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;
if (this.account.birthyear) {
age = currentYear - this.account.birthyear;
} else if (this.account.birthdate) {
const [year, month] = this.account.birthdate.split('-');
const before = parseInt(month, 10) > currentMonth;
age = Math.max(0, currentYear - parseInt(year, 10) - (before ? 1 : 0));
}
}
if (this.account.birthyear) {
age = currentYear - this.account.birthyear;
} else if (this.account.birthdate) {
const [year, month] = this.account.birthdate.split('-');
const before = parseInt(month, 10) > currentMonth;
age = Math.max(0, currentYear - parseInt(year, 10) - (before ? 1 : 0));
}
}
const params = new HttpParams()
.set('short', short.toString())
.set('d', age.toString())
.set('t', (Date.now() % 0x10000).toString(16));
const params = new HttpParams()
.set('short', short.toString())
.set('d', age.toString())
.set('t', (Date.now() % 0x10000).toString(16));
return observableToPromise(this.http.get<GameStatus>('/api2/game/status', { params }));
}
join(serverId: string, ponyId: string): Promise<JoinResponse> {
if (this.pending)
return Promise.reject(new Error('Joining in progress'));
if (!serverId)
return Promise.reject(new Error('Invalid server ID'));
if (!ponyId)
return Promise.reject(new Error('Invalid pony ID'));
return observableToPromise(this.http.get<GameStatus>('/api2/game/status', { params }));
}
join(serverId: string, ponyId: string): Promise<JoinResponse> {
if (this.pending)
return Promise.reject(new Error('Joining in progress'));
if (!serverId)
return Promise.reject(new Error('Invalid server ID'));
if (!ponyId)
return Promise.reject(new Error('Invalid pony ID'));
this.pending = true;
this.pending = true;
const alert = !!this.accountAlert ? 'y' : '';
const alert = !!this.accountAlert ? 'y' : '';
return this.post<JoinResponse>('/api/game/join', { version, ponyId, serverId, alert, url: location.href })
.finally(() => this.pending = false);
}
private post<T = void>(url: string, data: any, authenticate = true): Promise<T> {
if (authenticate) {
if (!this.account) {
return Promise.reject(new Error(NOT_AUTHENTICATED_ERROR));
}
return this.post<JoinResponse>('/api/game/join', { version, ponyId, serverId, alert, url: location.href })
.finally(() => this.pending = false);
}
private post<T = void>(url: string, data: any, authenticate = true): Promise<T> {
if (authenticate) {
if (!this.account) {
return Promise.reject(new Error(NOT_AUTHENTICATED_ERROR));
}
const accountId = this.account.id + this.suffix;
const accountName = this.account.name + this.suffix;
data = { accountId, accountName, ...data };
}
const accountId = this.account.id + this.suffix;
const accountName = this.account.name + this.suffix;
data = { accountId, accountName, ...data };
}
const params = new HttpParams()
.set('t', (Date.now() % 0x10000).toString(16));
const headers = new HttpHeaders({ 'api-version': HASH, 'api-bid': this.storage.getItem('bid') || '-' });
const params = new HttpParams()
.set('t', (Date.now() % 0x10000).toString(16));
const headers = new HttpHeaders({ 'api-version': HASH, 'api-bid': this.storage.getItem('bid') || '-' });
return observableToPromise(this.http.post<T>(url, data, { params, headers }));
}
return observableToPromise(this.http.post<T>(url, data, { params, headers }));
}
}
+78 -78
View File
@@ -9,99 +9,99 @@ import { MINUTE } from '../../common/constants';
type OnModel<T> = (item: T | undefined) => void;
interface ModelSubscriberConfig<T> {
fix?: (item: T) => void;
fix?: (item: T) => void;
}
interface ModelSubscription<T> {
value: T | undefined;
timeout: any;
callbacks: OnModel<T>[];
value: T | undefined;
timeout: any;
callbacks: OnModel<T>[];
}
const unsubscribeTimeout = 1 * MINUTE;
export class ModelSubscriber<T> {
private subscriptions = new Map<string, ModelSubscription<T>>();
// private observables = new Map<string, ModelSubscription<T>>();
constructor(
private type: ModelTypes,
private socket: SocketService<ClientAdminActions, IAdminServerActions>,
private config: ModelSubscriberConfig<T> = {},
private defaultValue: T | undefined = undefined,
) {
}
// for(id: string) {
// return this.createObservable(id);
// }
// private createObservable(id: string) {
// return new Observable<T | undefined>(observer => {
// this.socket.server.subscribe(this.model, id);
private subscriptions = new Map<string, ModelSubscription<T>>();
// private observables = new Map<string, ModelSubscription<T>>();
constructor(
private type: ModelTypes,
private socket: SocketService<ClientAdminActions, IAdminServerActions>,
private config: ModelSubscriberConfig<T> = {},
private defaultValue: T | undefined = undefined,
) {
}
// for(id: string) {
// return this.createObservable(id);
// }
// private createObservable(id: string) {
// return new Observable<T | undefined>(observer => {
// this.socket.server.subscribe(this.model, id);
// return () => {
// this.socket.server.unsubscribe(this.model, id);
// };
// });
// }
get(id: string) {
const subscription = this.subscriptions.get(id);
return subscription && subscription.value;
}
subscribe(id: string, callback: OnModel<T>): Subscription {
const subscription = this.subscriptions.get(id);
// return () => {
// this.socket.server.unsubscribe(this.model, id);
// };
// });
// }
get(id: string) {
const subscription = this.subscriptions.get(id);
return subscription && subscription.value;
}
subscribe(id: string, callback: OnModel<T>): Subscription {
const subscription = this.subscriptions.get(id);
if (subscription) {
if (subscription.timeout) {
clearTimeout(subscription.timeout);
subscription.timeout = 0;
}
if (subscription) {
if (subscription.timeout) {
clearTimeout(subscription.timeout);
subscription.timeout = 0;
}
subscription.callbacks.push(callback);
subscription.callbacks.push(callback);
if (subscription.value !== undefined) {
callback(subscription.value);
}
} else {
this.socket.server.subscribe(this.type, id);
this.subscriptions.set(id, {
value: this.defaultValue,
timeout: 0,
callbacks: [callback],
});
}
if (subscription.value !== undefined) {
callback(subscription.value);
}
} else {
this.socket.server.subscribe(this.type, id);
this.subscriptions.set(id, {
value: this.defaultValue,
timeout: 0,
callbacks: [callback],
});
}
return {
unsubscribe: () => this.unsubscribe(id, callback),
};
}
unsubscribe(id: string, callback: OnModel<T>) {
const subscription = this.subscriptions.get(id);
return {
unsubscribe: () => this.unsubscribe(id, callback),
};
}
unsubscribe(id: string, callback: OnModel<T>) {
const subscription = this.subscriptions.get(id);
if (subscription) {
removeItem(subscription.callbacks, callback);
if (subscription) {
removeItem(subscription.callbacks, callback);
if (subscription.callbacks.length === 0) {
subscription.timeout = setTimeout(() => {
this.socket.server.unsubscribe(this.type, id);
this.subscriptions.delete(id);
}, unsubscribeTimeout);
}
}
}
update(id: string, update: T) {
const subscription = this.subscriptions.get(id);
if (subscription.callbacks.length === 0) {
subscription.timeout = setTimeout(() => {
this.socket.server.unsubscribe(this.type, id);
this.subscriptions.delete(id);
}, unsubscribeTimeout);
}
}
}
update(id: string, update: T) {
const subscription = this.subscriptions.get(id);
if (update !== undefined && this.config.fix) {
this.config.fix(update);
}
if (update !== undefined && this.config.fix) {
this.config.fix(update);
}
if (subscription) {
subscription.value = update;
subscription.callbacks.forEach(c => c(update));
}
}
connected() {
this.subscriptions.forEach((_, id) => {
this.socket.server.subscribe(this.type, id);
});
}
if (subscription) {
subscription.value = update;
subscription.callbacks.forEach(c => c(update));
}
}
connected() {
this.subscriptions.forEach((_, id) => {
this.socket.server.subscribe(this.type, id);
});
}
}
@@ -8,54 +8,54 @@ import { rollbarCheckIgnore, isIgnoredError } from '../../common/rollbar';
const host = typeof location === 'undefined' ? '' : location.host;
const rollbarConfig = {
environment: ROLLBAR_ENV,
accessToken: ROLLBAR_TOKEN,
ignoredMessages: ['disconnected'],
hostWhiteList: [host],
captureUncaught: true,
captureUnhandleRejections: true,
// checkIgnore,
enabled: true,
payload: {
environment: ROLLBAR_ENV,
version: version, // NOTE: workaround for compilation issue
client: {
javascript: {
source_map_enabled: true,
guess_uncaught_frames: true,
code_version: HASH,
},
},
},
environment: ROLLBAR_ENV,
accessToken: ROLLBAR_TOKEN,
ignoredMessages: ['disconnected'],
hostWhiteList: [host],
captureUncaught: true,
captureUnhandleRejections: true,
// checkIgnore,
enabled: true,
payload: {
environment: ROLLBAR_ENV,
version: version, // NOTE: workaround for compilation issue
client: {
javascript: {
source_map_enabled: true,
guess_uncaught_frames: true,
code_version: HASH,
},
},
},
};
export const RollbarService = new InjectionToken<Rollbar>('rollbar');
export function rollbarFactory() {
if (DEVELOPMENT) {
return undefined;
} else {
const rollbar = Rollbar.init(rollbarConfig);
rollbar.configure({ checkIgnore: rollbarCheckIgnore });
return rollbar;
}
if (DEVELOPMENT) {
return undefined;
} else {
const rollbar = Rollbar.init(rollbarConfig);
rollbar.configure({ checkIgnore: rollbarCheckIgnore });
return rollbar;
}
}
@Injectable()
export class RollbarErrorHandler extends ErrorHandler {
constructor(private injector: Injector) {
super();
}
handleError(error: any) {
super.handleError(error);
constructor(private injector: Injector) {
super();
}
handleError(error: any) {
super.handleError(error);
if (!DEVELOPMENT && rollbarConfig.accessToken) {
const rollbar = this.injector.get(RollbarService);
const err = error.originalError || error || {};
if (!DEVELOPMENT && rollbarConfig.accessToken) {
const rollbar = this.injector.get(RollbarService);
const err = error.originalError || error || {};
if (!isIgnoredError(err)) {
rollbar.error(err);
}
}
}
if (!isIgnoredError(err)) {
rollbar.error(err);
}
}
}
}
@@ -6,34 +6,34 @@ import { ErrorReporter } from './errorReporter';
@Injectable()
export class RollbarErrorReporter extends ErrorReporter {
constructor(@Inject(RollbarService) private rollbar?: Rollbar) {
super();
}
configureUser(person: Person) {
if (this.rollbar) {
this.rollbar.configure({ payload: { person }, checkIgnore: rollbarCheckIgnore });
}
}
configureData(data: any) {
if (this.rollbar) {
this.rollbar.configure({ payload: data, checkIgnore: rollbarCheckIgnore });
}
}
captureEvent(data: any) {
if (this.rollbar) {
this.rollbar.captureEvent(data, 'info');
}
}
reportError(error: any, data?: any) {
DEVELOPMENT && console.error(error, data);
constructor(@Inject(RollbarService) private rollbar?: Rollbar) {
super();
}
configureUser(person: Person) {
if (this.rollbar) {
this.rollbar.configure({ payload: { person }, checkIgnore: rollbarCheckIgnore });
}
}
configureData(data: any) {
if (this.rollbar) {
this.rollbar.configure({ payload: data, checkIgnore: rollbarCheckIgnore });
}
}
captureEvent(data: any) {
if (this.rollbar) {
this.rollbar.captureEvent(data, 'info');
}
}
reportError(error: any, data?: any) {
DEVELOPMENT && console.error(error, data);
if (this.rollbar && !isIgnoredError(error)) {
this.rollbar.error(error, data);
}
}
disable() {
if (this.rollbar) {
this.rollbar.configure({ enabled: false });
}
}
if (this.rollbar && !isIgnoredError(error)) {
this.rollbar.error(error, data);
}
}
disable() {
if (this.rollbar) {
this.rollbar.configure({ enabled: false });
}
}
}
+33 -33
View File
@@ -5,39 +5,39 @@ import { Model } from './model';
@Injectable({ providedIn: 'root' })
export class SettingsService {
browser: BrowserSettings;
private save: (settings: AccountSettings) => boolean = () => false;
constructor(private storage: StorageService, private model: Model) {
this.browser = this.storage.getJSON('browser-settings', {});
}
get account(): AccountSettings {
return this.model.account ? this.model.account.settings : {};
}
set account(value) {
if (this.model.account) {
this.model.account.settings = value;
}
}
saving(save: (settings: AccountSettings) => boolean) {
this.save = save;
}
saveAccountSettings(settings: AccountSettings) {
if (this.model.account) {
this.model.account.settings = settings;
}
browser: BrowserSettings;
private save: (settings: AccountSettings) => boolean = () => false;
constructor(private storage: StorageService, private model: Model) {
this.browser = this.storage.getJSON('browser-settings', {});
}
get account(): AccountSettings {
return this.model.account ? this.model.account.settings : {};
}
set account(value) {
if (this.model.account) {
this.model.account.settings = value;
}
}
saving(save: (settings: AccountSettings) => boolean) {
this.save = save;
}
saveAccountSettings(settings: AccountSettings) {
if (this.model.account) {
this.model.account.settings = settings;
}
if (settings.filterWords) {
settings.filterWords = settings.filterWords.trim();
}
if (settings.filterWords) {
settings.filterWords = settings.filterWords.trim();
}
if (this.save(settings)) {
return Promise.resolve();
} else {
return this.model.saveSettings(settings);
}
}
saveBrowserSettings(settings?: BrowserSettings) {
this.browser = settings || this.browser;
this.storage.setJSON('browser-settings', this.browser);
}
if (this.save(settings)) {
return Promise.resolve();
} else {
return this.model.saveSettings(settings);
}
}
saveBrowserSettings(settings?: BrowserSettings) {
this.browser = settings || this.browser;
this.storage.setJSON('browser-settings', this.browser);
}
}
+77 -77
View File
@@ -3,82 +3,82 @@ import { Injectable } from '@angular/core';
/* istanbul ignore next */
@Injectable({ providedIn: 'root' })
export class StorageService {
private data?: Map<string, string> = undefined;
constructor() {
try {
if (typeof localStorage === 'undefined') {
this.data = new Map();
}
} catch {
this.data = new Map();
}
}
getItem(key: string) {
if (this.data) {
return this.data.get(key);
} else {
try {
const value = localStorage.getItem(key);
return value == null ? undefined : value;
} catch {
return undefined;
}
}
}
setItem(key: string, data: string) {
try {
localStorage.setItem(key, data);
this.data = undefined;
} catch {
if (!this.data) {
this.data = new Map();
}
private data?: Map<string, string> = undefined;
constructor() {
try {
if (typeof localStorage === 'undefined') {
this.data = new Map();
}
} catch {
this.data = new Map();
}
}
getItem(key: string) {
if (this.data) {
return this.data.get(key);
} else {
try {
const value = localStorage.getItem(key);
return value == null ? undefined : value;
} catch {
return undefined;
}
}
}
setItem(key: string, data: string) {
try {
localStorage.setItem(key, data);
this.data = undefined;
} catch {
if (!this.data) {
this.data = new Map();
}
this.data.set(key, data);
}
}
removeItem(key: string) {
if (this.data) {
this.data.delete(key);
} else {
try {
localStorage.removeItem(key);
} catch { }
}
}
clear() {
if (this.data) {
this.data.clear();
} else {
try {
localStorage.clear();
} catch { }
}
}
getJSON<T>(key: string, defaultValue: T): T {
try {
return JSON.parse(this.getItem(key) || '');
} catch {
return defaultValue;
}
}
setJSON(key: string, value: any) {
this.setItem(key, JSON.stringify(value));
}
getInt(key: string) {
return parseInt(this.getItem(key) || '0', 10) | 0;
}
setInt(key: string, value: number) {
this.setItem(key, value.toString(10));
}
getBoolean(key: string) {
return this.getItem(key) === 'true';
}
setBoolean(key: string, value: boolean) {
if (value) {
this.setItem(key, 'true');
} else {
this.removeItem(key);
}
}
this.data.set(key, data);
}
}
removeItem(key: string) {
if (this.data) {
this.data.delete(key);
} else {
try {
localStorage.removeItem(key);
} catch { }
}
}
clear() {
if (this.data) {
this.data.clear();
} else {
try {
localStorage.clear();
} catch { }
}
}
getJSON<T>(key: string, defaultValue: T): T {
try {
return JSON.parse(this.getItem(key) || '');
} catch {
return defaultValue;
}
}
setJSON(key: string, value: any) {
this.setItem(key, JSON.stringify(value));
}
getInt(key: string) {
return parseInt(this.getItem(key) || '0', 10) | 0;
}
setInt(key: string, value: number) {
this.setItem(key, value.toString(10));
}
getBoolean(key: string) {
return this.getItem(key) === 'true';
}
setBoolean(key: string, value: boolean) {
if (value) {
this.setItem(key, 'true');
} else {
this.removeItem(key);
}
}
}