mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
Change prettier rules
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"criticalErrorHappened": "There was a critical error due to which the application can not continue its normal operation.",
|
||||
"reloadPageOrContactUs": "Please reload this page and try again. If problem occurs again, please report it to the developers by sending email to",
|
||||
"alsoYouCanInteractWithBackground": "You can also play around with the background – it's interactable ;)"
|
||||
"criticalErrorHappened": "There was a critical error due to which the application can not continue its normal operation.",
|
||||
"reloadPageOrContactUs": "Please reload this page and try again. If problem occurs again, please report it to the developers by sending email to",
|
||||
"alsoYouCanInteractWithBackground": "You can also play around with the background – it's interactable ;)"
|
||||
}
|
||||
|
@@ -9,48 +9,45 @@ import styles from './styles.scss';
|
||||
import messages from './BSoD.intl.json';
|
||||
|
||||
interface State {
|
||||
lastEventId?: string | void;
|
||||
lastEventId?: string | void;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
lastEventId?: string;
|
||||
lastEventId?: string;
|
||||
}
|
||||
|
||||
// TODO: probably it's better to render this view from the App view
|
||||
// to remove dependencies from the store and IntlProvider
|
||||
const BSoD: ComponentType<Props> = ({ lastEventId }) => {
|
||||
let emailUrl = 'mailto:support@ely.by';
|
||||
let emailUrl = 'mailto:support@ely.by';
|
||||
|
||||
if (lastEventId) {
|
||||
emailUrl += `?subject=Bug report for #${lastEventId}`;
|
||||
}
|
||||
if (lastEventId) {
|
||||
emailUrl += `?subject=Bug report for #${lastEventId}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.body}>
|
||||
<canvas
|
||||
className={styles.canvas}
|
||||
ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)}
|
||||
/>
|
||||
return (
|
||||
<div className={styles.body}>
|
||||
<canvas className={styles.canvas} ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)} />
|
||||
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href={emailUrl} className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href={emailUrl} className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default BSoD;
|
||||
|
@@ -5,25 +5,25 @@ import logger from 'app/services/logger/logger';
|
||||
import BSoD from './BSoD';
|
||||
|
||||
const BSoDContainer: ComponentType = () => {
|
||||
const [lastEventId, setLastEventId] = useState<string>();
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const lastEventId = logger.getLastEventId();
|
||||
const [lastEventId, setLastEventId] = useState<string>();
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const lastEventId = logger.getLastEventId();
|
||||
|
||||
if (!lastEventId) {
|
||||
return;
|
||||
}
|
||||
if (!lastEventId) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(timer);
|
||||
setLastEventId(lastEventId);
|
||||
}, 500);
|
||||
clearInterval(timer);
|
||||
setLastEventId(lastEventId);
|
||||
}, 500);
|
||||
|
||||
// Don't care about interval cleanup because there is no way from
|
||||
// BSoD state and page can be only reloaded
|
||||
}, []);
|
||||
// Don't care about interval cleanup because there is no way from
|
||||
// BSoD state and page can be only reloaded
|
||||
}, []);
|
||||
|
||||
return <BSoD lastEventId={lastEventId} />;
|
||||
return <BSoD lastEventId={lastEventId} />;
|
||||
};
|
||||
|
||||
export default BSoDContainer;
|
||||
|
@@ -3,115 +3,109 @@ import Point from './Point';
|
||||
const shadowLength = 2000; // TODO: should be calculated
|
||||
|
||||
export default class Box {
|
||||
public position: Point;
|
||||
private angle: number;
|
||||
public color: string;
|
||||
private readonly shadowColor: string;
|
||||
public position: Point;
|
||||
private angle: number;
|
||||
public color: string;
|
||||
private readonly shadowColor: string;
|
||||
|
||||
private _size: number;
|
||||
private _halfSize: number;
|
||||
private _size: number;
|
||||
private _halfSize: number;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
position: Point,
|
||||
startRotate: number,
|
||||
color: string,
|
||||
shadowColor: string,
|
||||
) {
|
||||
this.size = size;
|
||||
this.position = position;
|
||||
this.color = color;
|
||||
this.angle = startRotate;
|
||||
this.shadowColor = shadowColor;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public set size(size: number) {
|
||||
this._size = size;
|
||||
this._halfSize = Math.floor(size / 2);
|
||||
}
|
||||
|
||||
public get halfSize(): number {
|
||||
return this._halfSize;
|
||||
}
|
||||
|
||||
get points(): { p1: Point; p2: Point; p3: Point; p4: Point } {
|
||||
const full = (Math.PI * 2) / 4;
|
||||
|
||||
const p1: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle),
|
||||
};
|
||||
|
||||
const p2: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full),
|
||||
};
|
||||
|
||||
const p3: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 2),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 2),
|
||||
};
|
||||
|
||||
const p4: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 3),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 3),
|
||||
};
|
||||
|
||||
return { p1, p2, p3, p4 };
|
||||
}
|
||||
|
||||
rotate(): void {
|
||||
const speed = (60 - this._halfSize) / 20;
|
||||
this.angle += speed * 0.002;
|
||||
this.position.x += speed;
|
||||
this.position.y += speed;
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D): void {
|
||||
const { points } = this;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points.p1.x, points.p1.y);
|
||||
ctx.lineTo(points.p2.x, points.p2.y);
|
||||
ctx.lineTo(points.p3.x, points.p3.y);
|
||||
ctx.lineTo(points.p4.x, points.p4.y);
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
drawShadow(ctx: CanvasRenderingContext2D, light: Point): void {
|
||||
const boxPoints = this.points;
|
||||
const points: Array<{
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
}> = [];
|
||||
|
||||
Object.values(boxPoints).forEach((point) => {
|
||||
const angle = Math.atan2(light.y - point.y, light.x - point.x);
|
||||
const endX = point.x + shadowLength * Math.sin(-angle - Math.PI / 2);
|
||||
const endY = point.y + shadowLength * Math.cos(-angle - Math.PI / 2);
|
||||
points.push({
|
||||
startX: point.x,
|
||||
startY: point.y,
|
||||
endX,
|
||||
endY,
|
||||
});
|
||||
});
|
||||
|
||||
for (let i = points.length - 1; i >= 0; i--) {
|
||||
const n = i === 3 ? 0 : i + 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[i].startX, points[i].startY);
|
||||
ctx.lineTo(points[n].startX, points[n].startY);
|
||||
ctx.lineTo(points[n].endX, points[n].endY);
|
||||
ctx.lineTo(points[i].endX, points[i].endY);
|
||||
ctx.fillStyle = this.shadowColor;
|
||||
ctx.fill();
|
||||
constructor(size: number, position: Point, startRotate: number, color: string, shadowColor: string) {
|
||||
this.size = size;
|
||||
this.position = position;
|
||||
this.color = color;
|
||||
this.angle = startRotate;
|
||||
this.shadowColor = shadowColor;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public set size(size: number) {
|
||||
this._size = size;
|
||||
this._halfSize = Math.floor(size / 2);
|
||||
}
|
||||
|
||||
public get halfSize(): number {
|
||||
return this._halfSize;
|
||||
}
|
||||
|
||||
get points(): { p1: Point; p2: Point; p3: Point; p4: Point } {
|
||||
const full = (Math.PI * 2) / 4;
|
||||
|
||||
const p1: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle),
|
||||
};
|
||||
|
||||
const p2: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full),
|
||||
};
|
||||
|
||||
const p3: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 2),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 2),
|
||||
};
|
||||
|
||||
const p4: Point = {
|
||||
x: this.position.x + this._halfSize * Math.sin(this.angle + full * 3),
|
||||
y: this.position.y + this._halfSize * Math.cos(this.angle + full * 3),
|
||||
};
|
||||
|
||||
return { p1, p2, p3, p4 };
|
||||
}
|
||||
|
||||
rotate(): void {
|
||||
const speed = (60 - this._halfSize) / 20;
|
||||
this.angle += speed * 0.002;
|
||||
this.position.x += speed;
|
||||
this.position.y += speed;
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D): void {
|
||||
const { points } = this;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points.p1.x, points.p1.y);
|
||||
ctx.lineTo(points.p2.x, points.p2.y);
|
||||
ctx.lineTo(points.p3.x, points.p3.y);
|
||||
ctx.lineTo(points.p4.x, points.p4.y);
|
||||
ctx.fillStyle = this.color;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
drawShadow(ctx: CanvasRenderingContext2D, light: Point): void {
|
||||
const boxPoints = this.points;
|
||||
const points: Array<{
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
}> = [];
|
||||
|
||||
Object.values(boxPoints).forEach((point) => {
|
||||
const angle = Math.atan2(light.y - point.y, light.x - point.x);
|
||||
const endX = point.x + shadowLength * Math.sin(-angle - Math.PI / 2);
|
||||
const endY = point.y + shadowLength * Math.cos(-angle - Math.PI / 2);
|
||||
points.push({
|
||||
startX: point.x,
|
||||
startY: point.y,
|
||||
endX,
|
||||
endY,
|
||||
});
|
||||
});
|
||||
|
||||
for (let i = points.length - 1; i >= 0; i--) {
|
||||
const n = i === 3 ? 0 : i + 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[i].startX, points[i].startY);
|
||||
ctx.lineTo(points[n].startX, points[n].startY);
|
||||
ctx.lineTo(points[n].endX, points[n].endY);
|
||||
ctx.lineTo(points[i].endX, points[i].endY);
|
||||
ctx.fillStyle = this.shadowColor;
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,177 +2,157 @@ import Point from './Point';
|
||||
import Box from './Box';
|
||||
|
||||
interface Params {
|
||||
countBoxes: number;
|
||||
boxMinSize: number;
|
||||
boxMaxSize: number;
|
||||
backgroundColor: string;
|
||||
lightColor: string;
|
||||
shadowColor: string;
|
||||
boxColors: ReadonlyArray<string>;
|
||||
countBoxes: number;
|
||||
boxMinSize: number;
|
||||
boxMaxSize: number;
|
||||
backgroundColor: string;
|
||||
lightColor: string;
|
||||
shadowColor: string;
|
||||
boxColors: ReadonlyArray<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on http://codepen.io/mladen___/pen/gbvqBo
|
||||
*/
|
||||
export default class BoxesField {
|
||||
private readonly elem: HTMLCanvasElement;
|
||||
private readonly ctx: CanvasRenderingContext2D;
|
||||
private readonly params: Params;
|
||||
private readonly elem: HTMLCanvasElement;
|
||||
private readonly ctx: CanvasRenderingContext2D;
|
||||
private readonly params: Params;
|
||||
|
||||
private light: Point;
|
||||
private boxes: Array<Box>;
|
||||
private light: Point;
|
||||
private boxes: Array<Box>;
|
||||
|
||||
/**
|
||||
* @param {HTMLCanvasElement} elem - canvas DOM node
|
||||
* @param {object} params
|
||||
*/
|
||||
constructor(
|
||||
elem: HTMLCanvasElement,
|
||||
params: Params = {
|
||||
countBoxes: 14,
|
||||
boxMinSize: 20,
|
||||
boxMaxSize: 75,
|
||||
backgroundColor: '#233d49',
|
||||
lightColor: '#28555b',
|
||||
shadowColor: '#274451',
|
||||
boxColors: [
|
||||
'#207e5c',
|
||||
'#5b9aa9',
|
||||
'#e66c69',
|
||||
'#6b5b8c',
|
||||
'#8b5d79',
|
||||
'#dd8650',
|
||||
],
|
||||
},
|
||||
) {
|
||||
this.elem = elem;
|
||||
const ctx = elem.getContext('2d');
|
||||
/**
|
||||
* @param {HTMLCanvasElement} elem - canvas DOM node
|
||||
* @param {object} params
|
||||
*/
|
||||
constructor(
|
||||
elem: HTMLCanvasElement,
|
||||
params: Params = {
|
||||
countBoxes: 14,
|
||||
boxMinSize: 20,
|
||||
boxMaxSize: 75,
|
||||
backgroundColor: '#233d49',
|
||||
lightColor: '#28555b',
|
||||
shadowColor: '#274451',
|
||||
boxColors: ['#207e5c', '#5b9aa9', '#e66c69', '#6b5b8c', '#8b5d79', '#dd8650'],
|
||||
},
|
||||
) {
|
||||
this.elem = elem;
|
||||
const ctx = elem.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error('Cannot get canvas 2d context');
|
||||
if (!ctx) {
|
||||
throw new Error('Cannot get canvas 2d context');
|
||||
}
|
||||
|
||||
this.ctx = ctx;
|
||||
this.params = params;
|
||||
|
||||
this.light = {
|
||||
x: 160,
|
||||
y: 200,
|
||||
};
|
||||
|
||||
this.resize();
|
||||
this.drawLoop();
|
||||
this.bindWindowListeners();
|
||||
|
||||
this.boxes = [];
|
||||
|
||||
while (this.boxes.length < this.params.countBoxes) {
|
||||
this.boxes.push(
|
||||
new Box(
|
||||
Math.floor(
|
||||
Math.random() * (this.params.boxMaxSize - this.params.boxMinSize) + this.params.boxMinSize,
|
||||
),
|
||||
{
|
||||
x: Math.floor(Math.random() * elem.width + 1),
|
||||
y: Math.floor(Math.random() * elem.height + 1),
|
||||
},
|
||||
Math.random() * Math.PI,
|
||||
this.getRandomColor(),
|
||||
this.params.shadowColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx = ctx;
|
||||
this.params = params;
|
||||
|
||||
this.light = {
|
||||
x: 160,
|
||||
y: 200,
|
||||
};
|
||||
|
||||
this.resize();
|
||||
this.drawLoop();
|
||||
this.bindWindowListeners();
|
||||
|
||||
this.boxes = [];
|
||||
|
||||
while (this.boxes.length < this.params.countBoxes) {
|
||||
this.boxes.push(
|
||||
new Box(
|
||||
Math.floor(
|
||||
Math.random() * (this.params.boxMaxSize - this.params.boxMinSize) +
|
||||
this.params.boxMinSize,
|
||||
),
|
||||
{
|
||||
x: Math.floor(Math.random() * elem.width + 1),
|
||||
y: Math.floor(Math.random() * elem.height + 1),
|
||||
},
|
||||
Math.random() * Math.PI,
|
||||
this.getRandomColor(),
|
||||
this.params.shadowColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
resize(): void {
|
||||
const { width, height } = this.elem.getBoundingClientRect();
|
||||
this.elem.width = width;
|
||||
this.elem.height = height;
|
||||
}
|
||||
|
||||
drawLight(light: Point): void {
|
||||
const greaterSize =
|
||||
window.screen.width > window.screen.height
|
||||
? window.screen.width
|
||||
: window.screen.height;
|
||||
// еее, теорема пифагора и описывание окружности вокруг квадрата, не зря в универ ходил!!!
|
||||
const lightRadius = greaterSize * Math.sqrt(2);
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(light.x, light.y, lightRadius, 0, 2 * Math.PI);
|
||||
const gradient = this.ctx.createRadialGradient(
|
||||
light.x,
|
||||
light.y,
|
||||
0,
|
||||
light.x,
|
||||
light.y,
|
||||
lightRadius,
|
||||
);
|
||||
gradient.addColorStop(0, this.params.lightColor);
|
||||
gradient.addColorStop(1, this.params.backgroundColor);
|
||||
this.ctx.fillStyle = gradient;
|
||||
this.ctx.fill();
|
||||
}
|
||||
|
||||
drawLoop(): void {
|
||||
this.ctx.clearRect(0, 0, this.elem.width, this.elem.height);
|
||||
this.drawLight(this.light);
|
||||
|
||||
for (const i in this.boxes) {
|
||||
if (!this.boxes.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const box = this.boxes[i];
|
||||
box.rotate();
|
||||
box.drawShadow(this.ctx, this.light);
|
||||
resize(): void {
|
||||
const { width, height } = this.elem.getBoundingClientRect();
|
||||
this.elem.width = width;
|
||||
this.elem.height = height;
|
||||
}
|
||||
|
||||
for (const i in this.boxes) {
|
||||
if (!this.boxes.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
drawLight(light: Point): void {
|
||||
const greaterSize = window.screen.width > window.screen.height ? window.screen.width : window.screen.height;
|
||||
// еее, теорема пифагора и описывание окружности вокруг квадрата, не зря в универ ходил!!!
|
||||
const lightRadius = greaterSize * Math.sqrt(2);
|
||||
|
||||
const box = this.boxes[i];
|
||||
box.draw(this.ctx);
|
||||
|
||||
// When box leaves window boundaries
|
||||
let shouldUpdateBox = false;
|
||||
|
||||
if (box.position.y - box.halfSize > this.elem.height) {
|
||||
box.position.y -= this.elem.height + 100;
|
||||
shouldUpdateBox = true;
|
||||
}
|
||||
|
||||
if (box.position.x - box.halfSize > this.elem.width) {
|
||||
box.position.x -= this.elem.width + 100;
|
||||
shouldUpdateBox = true;
|
||||
}
|
||||
|
||||
if (shouldUpdateBox) {
|
||||
this.updateBox(box);
|
||||
}
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(light.x, light.y, lightRadius, 0, 2 * Math.PI);
|
||||
const gradient = this.ctx.createRadialGradient(light.x, light.y, 0, light.x, light.y, lightRadius);
|
||||
gradient.addColorStop(0, this.params.lightColor);
|
||||
gradient.addColorStop(1, this.params.backgroundColor);
|
||||
this.ctx.fillStyle = gradient;
|
||||
this.ctx.fill();
|
||||
}
|
||||
|
||||
requestAnimationFrame(this.drawLoop.bind(this));
|
||||
}
|
||||
drawLoop(): void {
|
||||
this.ctx.clearRect(0, 0, this.elem.width, this.elem.height);
|
||||
this.drawLight(this.light);
|
||||
|
||||
bindWindowListeners() {
|
||||
window.addEventListener('resize', this.resize.bind(this));
|
||||
window.addEventListener('mousemove', (event) => {
|
||||
this.light.x = event.clientX;
|
||||
this.light.y = event.clientY;
|
||||
});
|
||||
}
|
||||
for (const i in this.boxes) {
|
||||
if (!this.boxes.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
updateBox(box: Box): void {
|
||||
box.color = this.getRandomColor();
|
||||
}
|
||||
const box = this.boxes[i];
|
||||
box.rotate();
|
||||
box.drawShadow(this.ctx, this.light);
|
||||
}
|
||||
|
||||
getRandomColor(): string {
|
||||
return this.params.boxColors[
|
||||
Math.floor(Math.random() * this.params.boxColors.length)
|
||||
];
|
||||
}
|
||||
for (const i in this.boxes) {
|
||||
if (!this.boxes.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const box = this.boxes[i];
|
||||
box.draw(this.ctx);
|
||||
|
||||
// When box leaves window boundaries
|
||||
let shouldUpdateBox = false;
|
||||
|
||||
if (box.position.y - box.halfSize > this.elem.height) {
|
||||
box.position.y -= this.elem.height + 100;
|
||||
shouldUpdateBox = true;
|
||||
}
|
||||
|
||||
if (box.position.x - box.halfSize > this.elem.width) {
|
||||
box.position.x -= this.elem.width + 100;
|
||||
shouldUpdateBox = true;
|
||||
}
|
||||
|
||||
if (shouldUpdateBox) {
|
||||
this.updateBox(box);
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(this.drawLoop.bind(this));
|
||||
}
|
||||
|
||||
bindWindowListeners() {
|
||||
window.addEventListener('resize', this.resize.bind(this));
|
||||
window.addEventListener('mousemove', (event) => {
|
||||
this.light.x = event.clientX;
|
||||
this.light.y = event.clientY;
|
||||
});
|
||||
}
|
||||
|
||||
updateBox(box: Box): void {
|
||||
box.color = this.getRandomColor();
|
||||
}
|
||||
|
||||
getRandomColor(): string {
|
||||
return this.params.boxColors[Math.floor(Math.random() * this.params.boxColors.length)];
|
||||
}
|
||||
}
|
||||
|
@@ -4,46 +4,39 @@ import sinon from 'sinon';
|
||||
import BsodMiddleware from 'app/components/ui/bsod/BsodMiddleware';
|
||||
|
||||
describe('BsodMiddleware', () => {
|
||||
[500, 503, 555].forEach((code) =>
|
||||
it(`should dispatch for ${code}`, () => {
|
||||
const resp = {
|
||||
originalResponse: { status: code },
|
||||
};
|
||||
[500, 503, 555].forEach((code) =>
|
||||
it(`should dispatch for ${code}`, () => {
|
||||
const resp = {
|
||||
originalResponse: { status: code },
|
||||
};
|
||||
|
||||
const dispatchBsod = sinon.spy();
|
||||
const logger = { warn: sinon.spy() };
|
||||
const dispatchBsod = sinon.spy();
|
||||
const logger = { warn: sinon.spy() };
|
||||
|
||||
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
|
||||
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
|
||||
|
||||
return expect(middleware.catch(resp), 'to be rejected with', resp).then(
|
||||
() => {
|
||||
expect(dispatchBsod, 'was called');
|
||||
expect(logger.warn, 'to have a call satisfying', [
|
||||
'Unexpected response (BSoD)',
|
||||
{ resp },
|
||||
]);
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
return expect(middleware.catch(resp), 'to be rejected with', resp).then(() => {
|
||||
expect(dispatchBsod, 'was called');
|
||||
expect(logger.warn, 'to have a call satisfying', ['Unexpected response (BSoD)', { resp }]);
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
[200, 404].forEach((code) =>
|
||||
it(`should not dispatch for ${code}`, () => {
|
||||
const resp = {
|
||||
originalResponse: { status: code },
|
||||
};
|
||||
[200, 404].forEach((code) =>
|
||||
it(`should not dispatch for ${code}`, () => {
|
||||
const resp = {
|
||||
originalResponse: { status: code },
|
||||
};
|
||||
|
||||
const dispatchBsod = sinon.spy();
|
||||
const logger = { warn: sinon.spy() };
|
||||
const dispatchBsod = sinon.spy();
|
||||
const logger = { warn: sinon.spy() };
|
||||
|
||||
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
|
||||
const middleware = new BsodMiddleware(dispatchBsod, logger as any);
|
||||
|
||||
return expect(middleware.catch(resp), 'to be rejected with', resp).then(
|
||||
() => {
|
||||
expect(dispatchBsod, 'was not called');
|
||||
expect(logger.warn, 'was not called');
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
return expect(middleware.catch(resp), 'to be rejected with', resp).then(() => {
|
||||
expect(dispatchBsod, 'was not called');
|
||||
expect(logger.warn, 'was not called');
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
@@ -7,42 +7,39 @@ type Logger = typeof defaultLogger;
|
||||
const ABORT_ERR = 20;
|
||||
|
||||
class BsodMiddleware implements Middleware {
|
||||
dispatchBsod: () => any;
|
||||
logger: Logger;
|
||||
dispatchBsod: () => any;
|
||||
logger: Logger;
|
||||
|
||||
constructor(dispatchBsod: () => any, logger: Logger = defaultLogger) {
|
||||
this.dispatchBsod = dispatchBsod;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async catch<T extends Resp<any>>(
|
||||
resp?: T | InternalServerError | Error,
|
||||
): Promise<T> {
|
||||
const { originalResponse } = (resp || {}) as InternalServerError;
|
||||
|
||||
if (
|
||||
resp &&
|
||||
((resp instanceof InternalServerError &&
|
||||
(resp.error as any).code !== ABORT_ERR) ||
|
||||
(originalResponse && /5\d\d/.test(originalResponse.status)))
|
||||
) {
|
||||
this.dispatchBsod();
|
||||
|
||||
const { message: errorMessage } = resp as { [key: string]: any };
|
||||
|
||||
if (!errorMessage || !/NetworkError/.test(errorMessage)) {
|
||||
let message = 'Unexpected response (BSoD)';
|
||||
|
||||
if (errorMessage) {
|
||||
message = `BSoD: ${errorMessage}`;
|
||||
}
|
||||
|
||||
this.logger.warn(message, { resp });
|
||||
}
|
||||
constructor(dispatchBsod: () => any, logger: Logger = defaultLogger) {
|
||||
this.dispatchBsod = dispatchBsod;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
return Promise.reject(resp);
|
||||
}
|
||||
async catch<T extends Resp<any>>(resp?: T | InternalServerError | Error): Promise<T> {
|
||||
const { originalResponse } = (resp || {}) as InternalServerError;
|
||||
|
||||
if (
|
||||
resp &&
|
||||
((resp instanceof InternalServerError && (resp.error as any).code !== ABORT_ERR) ||
|
||||
(originalResponse && /5\d\d/.test(originalResponse.status)))
|
||||
) {
|
||||
this.dispatchBsod();
|
||||
|
||||
const { message: errorMessage } = resp as { [key: string]: any };
|
||||
|
||||
if (!errorMessage || !/NetworkError/.test(errorMessage)) {
|
||||
let message = 'Unexpected response (BSoD)';
|
||||
|
||||
if (errorMessage) {
|
||||
message = `BSoD: ${errorMessage}`;
|
||||
}
|
||||
|
||||
this.logger.warn(message, { resp });
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(resp);
|
||||
}
|
||||
}
|
||||
|
||||
export default BsodMiddleware;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export default interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { Action as ReduxAction } from 'redux';
|
||||
|
||||
interface BSoDAction extends ReduxAction {
|
||||
type: 'BSOD';
|
||||
type: 'BSOD';
|
||||
}
|
||||
|
||||
export function bsod(): BSoDAction {
|
||||
return {
|
||||
type: 'BSOD',
|
||||
};
|
||||
return {
|
||||
type: 'BSOD',
|
||||
};
|
||||
}
|
||||
|
||||
export type Action = BSoDAction;
|
||||
|
@@ -11,31 +11,28 @@ let injectedStore: Store;
|
||||
let injectedHistory: History<any>;
|
||||
let onBsod: undefined | (() => void);
|
||||
|
||||
export default function dispatchBsod(
|
||||
store = injectedStore,
|
||||
history = injectedHistory,
|
||||
) {
|
||||
store.dispatch(bsod());
|
||||
onBsod && onBsod();
|
||||
export default function dispatchBsod(store = injectedStore, history = injectedHistory) {
|
||||
store.dispatch(bsod());
|
||||
onBsod && onBsod();
|
||||
|
||||
ReactDOM.render(
|
||||
<ContextProvider store={store} history={history}>
|
||||
<BSoDContainer />
|
||||
</ContextProvider>,
|
||||
document.getElementById('app'),
|
||||
);
|
||||
ReactDOM.render(
|
||||
<ContextProvider store={store} history={history}>
|
||||
<BSoDContainer />
|
||||
</ContextProvider>,
|
||||
document.getElementById('app'),
|
||||
);
|
||||
}
|
||||
|
||||
export function inject({
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
}: {
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
}) {
|
||||
injectedStore = store;
|
||||
injectedHistory = history;
|
||||
onBsod = stopLoading;
|
||||
injectedStore = store;
|
||||
injectedHistory = history;
|
||||
onBsod = stopLoading;
|
||||
}
|
||||
|
@@ -7,16 +7,16 @@ import dispatchBsod, { inject } from './dispatchBsod';
|
||||
import BsodMiddleware from './BsodMiddleware';
|
||||
|
||||
export default function factory({
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
}: {
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
}) {
|
||||
inject({ store, history, stopLoading });
|
||||
inject({ store, history, stopLoading });
|
||||
|
||||
// do bsod for 500/404 errors
|
||||
request.addMiddleware(new BsodMiddleware(dispatchBsod, logger));
|
||||
// do bsod for 500/404 errors
|
||||
request.addMiddleware(new BsodMiddleware(dispatchBsod, logger));
|
||||
}
|
||||
|
@@ -3,9 +3,9 @@ import { Action } from './actions';
|
||||
export type State = boolean;
|
||||
|
||||
export default function (state: State = false, { type }: Action): State {
|
||||
if (type === 'BSOD') {
|
||||
return true;
|
||||
}
|
||||
if (type === 'BSOD') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return state;
|
||||
return state;
|
||||
}
|
||||
|
@@ -1,64 +1,63 @@
|
||||
@import '~app/components/ui/colors.scss';
|
||||
|
||||
$font-family-monospaced: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Roboto Mono',
|
||||
monospace;
|
||||
$font-family-monospaced: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Roboto Mono', monospace;
|
||||
|
||||
.body {
|
||||
height: 100%;
|
||||
background-color: $dark_blue;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: $font-family-monospaced;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
background-color: $dark_blue;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-family: $font-family-monospaced;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
margin: 85px auto 0;
|
||||
max-width: 500px;
|
||||
padding: 0 20px;
|
||||
position: relative;
|
||||
margin: 85px auto 0;
|
||||
max-width: 500px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 26px;
|
||||
margin-bottom: 13px;
|
||||
font-size: 26px;
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 auto;
|
||||
font-size: 16px;
|
||||
color: #ebe8e1;
|
||||
margin: 0 auto;
|
||||
font-size: 16px;
|
||||
color: #ebe8e1;
|
||||
}
|
||||
|
||||
.lineWithMargin {
|
||||
composes: line;
|
||||
composes: line;
|
||||
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.support {
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
color: #fff;
|
||||
margin: 5px 0 44px;
|
||||
display: inline-block;
|
||||
border-bottom-color: #39777f;
|
||||
|
||||
&:hover {
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
color: #fff;
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
margin: 5px 0 44px;
|
||||
display: inline-block;
|
||||
border-bottom-color: #39777f;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.easterEgg {
|
||||
font-size: 14px;
|
||||
color: #ebe8e1;
|
||||
font-size: 14px;
|
||||
color: #ebe8e1;
|
||||
}
|
||||
|
Reference in New Issue
Block a user