mirror of
				https://github.com/elyby/accounts-frontend.git
				synced 2025-05-31 14:11:58 +05:30 
			
		
		
		
	Add typings for Box and BoxesField classes, split BSoD view into controller and pure view, add storybook, fix support link styles
This commit is contained in:
		
							
								
								
									
										6
									
								
								packages/app/components/ui/bsod/BSoD.story.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/app/components/ui/bsod/BSoD.story.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { storiesOf } from '@storybook/react';
 | 
			
		||||
 | 
			
		||||
import BSoD from './BSoD';
 | 
			
		||||
 | 
			
		||||
storiesOf('UI', module).add('BSoD', () => <BSoD />);
 | 
			
		||||
@@ -1,72 +1,56 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { ComponentType } from 'react';
 | 
			
		||||
import { FormattedMessage as Message } from 'react-intl';
 | 
			
		||||
import logger from 'app/services/logger';
 | 
			
		||||
 | 
			
		||||
import appInfo from 'app/components/auth/appInfo/AppInfo.intl.json';
 | 
			
		||||
 | 
			
		||||
import styles from './styles.scss';
 | 
			
		||||
import BoxesField from './BoxesField';
 | 
			
		||||
 | 
			
		||||
import styles from './styles.scss';
 | 
			
		||||
import messages from './BSoD.intl.json';
 | 
			
		||||
 | 
			
		||||
interface State {
 | 
			
		||||
  lastEventId?: string | void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: probably it is better to render this view from the App view
 | 
			
		||||
// to remove dependencies from store and IntlProvider
 | 
			
		||||
class BSoD extends React.Component<{}, State> {
 | 
			
		||||
  state: State = {};
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    // poll for event id
 | 
			
		||||
    const timer = setInterval(() => {
 | 
			
		||||
      if (!logger.getLastEventId()) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      clearInterval(timer);
 | 
			
		||||
 | 
			
		||||
      this.setState({
 | 
			
		||||
        lastEventId: logger.getLastEventId(),
 | 
			
		||||
      });
 | 
			
		||||
    }, 500);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { lastEventId } = this.state;
 | 
			
		||||
 | 
			
		||||
    let emailUrl = 'mailto:support@ely.by';
 | 
			
		||||
 | 
			
		||||
    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)}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <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>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
interface Props {
 | 
			
		||||
  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';
 | 
			
		||||
 | 
			
		||||
  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)}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default BSoD;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								packages/app/components/ui/bsod/BSoDContainer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								packages/app/components/ui/bsod/BSoDContainer.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import React, { ComponentType, useEffect, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
      if (!lastEventId) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return <BSoD lastEventId={lastEventId} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default BSoDContainer;
 | 
			
		||||
@@ -1,100 +0,0 @@
 | 
			
		||||
export default class Box {
 | 
			
		||||
  constructor({ size, startX, startY, startRotate, color, shadowColor }) {
 | 
			
		||||
    this.color = color;
 | 
			
		||||
    this.shadowColor = shadowColor;
 | 
			
		||||
    this.halfSize = 0;
 | 
			
		||||
    this.setSize(size);
 | 
			
		||||
    this.x = startX;
 | 
			
		||||
    this.y = startY;
 | 
			
		||||
    this.angle = startRotate;
 | 
			
		||||
    this.shadowLength = 2000; // TODO: should be calculated
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get size() {
 | 
			
		||||
    return this._initialSize;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get dots() {
 | 
			
		||||
    const full = (Math.PI * 2) / 4;
 | 
			
		||||
 | 
			
		||||
    const p1 = {
 | 
			
		||||
      x: this.x + this.halfSize * Math.sin(this.angle),
 | 
			
		||||
      y: this.y + this.halfSize * Math.cos(this.angle),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const p2 = {
 | 
			
		||||
      x: this.x + this.halfSize * Math.sin(this.angle + full),
 | 
			
		||||
      y: this.y + this.halfSize * Math.cos(this.angle + full),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const p3 = {
 | 
			
		||||
      x: this.x + this.halfSize * Math.sin(this.angle + full * 2),
 | 
			
		||||
      y: this.y + this.halfSize * Math.cos(this.angle + full * 2),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const p4 = {
 | 
			
		||||
      x: this.x + this.halfSize * Math.sin(this.angle + full * 3),
 | 
			
		||||
      y: this.y + this.halfSize * Math.cos(this.angle + full * 3),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return { p1, p2, p3, p4 };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rotate() {
 | 
			
		||||
    const speed = (60 - this.halfSize) / 20;
 | 
			
		||||
    this.angle += speed * 0.002;
 | 
			
		||||
    this.x += speed;
 | 
			
		||||
    this.y += speed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  draw(ctx) {
 | 
			
		||||
    const { dots } = this;
 | 
			
		||||
    ctx.beginPath();
 | 
			
		||||
    ctx.moveTo(dots.p1.x, dots.p1.y);
 | 
			
		||||
    ctx.lineTo(dots.p2.x, dots.p2.y);
 | 
			
		||||
    ctx.lineTo(dots.p3.x, dots.p3.y);
 | 
			
		||||
    ctx.lineTo(dots.p4.x, dots.p4.y);
 | 
			
		||||
    ctx.fillStyle = this.color;
 | 
			
		||||
    ctx.fill();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawShadow(ctx, light) {
 | 
			
		||||
    const { dots } = this;
 | 
			
		||||
    const angles = [];
 | 
			
		||||
    const points = [];
 | 
			
		||||
 | 
			
		||||
    for (const i in dots) {
 | 
			
		||||
      if (!dots.hasOwnProperty(i)) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const dot = dots[i];
 | 
			
		||||
      const angle = Math.atan2(light.y - dot.y, light.x - dot.x);
 | 
			
		||||
      const endX = dot.x + this.shadowLength * Math.sin(-angle - Math.PI / 2);
 | 
			
		||||
      const endY = dot.y + this.shadowLength * Math.cos(-angle - Math.PI / 2);
 | 
			
		||||
      angles.push(angle);
 | 
			
		||||
      points.push({
 | 
			
		||||
        endX,
 | 
			
		||||
        endY,
 | 
			
		||||
        startX: dot.x,
 | 
			
		||||
        startY: dot.y,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setSize(size) {
 | 
			
		||||
    this._initialSize = size;
 | 
			
		||||
    this.halfSize = Math.floor(size / 2);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								packages/app/components/ui/bsod/Box.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								packages/app/components/ui/bsod/Box.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    }> = [];
 | 
			
		||||
 | 
			
		||||
    // eslint-disable-next-line guard-for-in
 | 
			
		||||
    for (const i in boxPoints) {
 | 
			
		||||
      const point = boxPoints[i];
 | 
			
		||||
      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();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,34 @@
 | 
			
		||||
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>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Основано на http://codepen.io/mladen___/pen/gbvqBo
 | 
			
		||||
 * 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 light: Point;
 | 
			
		||||
  private boxes: Array<Box>;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {HTMLCanvasElement} elem - canvas DOM node
 | 
			
		||||
   * @param {object} params
 | 
			
		||||
   */
 | 
			
		||||
  constructor(
 | 
			
		||||
    elem,
 | 
			
		||||
    params = {
 | 
			
		||||
    elem: HTMLCanvasElement,
 | 
			
		||||
    params: Params = {
 | 
			
		||||
      countBoxes: 14,
 | 
			
		||||
      boxMinSize: 20,
 | 
			
		||||
      boxMaxSize: 75,
 | 
			
		||||
@@ -31,7 +49,7 @@ export default class BoxesField {
 | 
			
		||||
    const ctx = elem.getContext('2d');
 | 
			
		||||
 | 
			
		||||
    if (!ctx) {
 | 
			
		||||
      throw new Error('Can not get canvas 2d context');
 | 
			
		||||
      throw new Error('Cannot get canvas 2d context');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.ctx = ctx;
 | 
			
		||||
@@ -46,35 +64,34 @@ export default class BoxesField {
 | 
			
		||||
    this.drawLoop();
 | 
			
		||||
    this.bindWindowListeners();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {Box[]}
 | 
			
		||||
     */
 | 
			
		||||
    this.boxes = [];
 | 
			
		||||
 | 
			
		||||
    while (this.boxes.length < this.params.countBoxes) {
 | 
			
		||||
      this.boxes.push(
 | 
			
		||||
        new Box({
 | 
			
		||||
          size: Math.floor(
 | 
			
		||||
        new Box(
 | 
			
		||||
          Math.floor(
 | 
			
		||||
            Math.random() * (this.params.boxMaxSize - this.params.boxMinSize) +
 | 
			
		||||
              this.params.boxMinSize,
 | 
			
		||||
          ),
 | 
			
		||||
          startX: Math.floor(Math.random() * elem.width + 1),
 | 
			
		||||
          startY: Math.floor(Math.random() * elem.height + 1),
 | 
			
		||||
          startRotate: Math.random() * Math.PI,
 | 
			
		||||
          color: this.getRandomColor(),
 | 
			
		||||
          shadowColor: this.params.shadowColor,
 | 
			
		||||
        }),
 | 
			
		||||
          {
 | 
			
		||||
            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() {
 | 
			
		||||
  resize(): void {
 | 
			
		||||
    const { width, height } = this.elem.getBoundingClientRect();
 | 
			
		||||
    this.elem.width = width;
 | 
			
		||||
    this.elem.height = height;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawLight(light) {
 | 
			
		||||
  drawLight(light: Point): void {
 | 
			
		||||
    const greaterSize =
 | 
			
		||||
      window.screen.width > window.screen.height
 | 
			
		||||
        ? window.screen.width
 | 
			
		||||
@@ -98,7 +115,7 @@ export default class BoxesField {
 | 
			
		||||
    this.ctx.fill();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawLoop() {
 | 
			
		||||
  drawLoop(): void {
 | 
			
		||||
    this.ctx.clearRect(0, 0, this.elem.width, this.elem.height);
 | 
			
		||||
    this.drawLight(this.light);
 | 
			
		||||
 | 
			
		||||
@@ -120,14 +137,20 @@ export default class BoxesField {
 | 
			
		||||
      const box = this.boxes[i];
 | 
			
		||||
      box.draw(this.ctx);
 | 
			
		||||
 | 
			
		||||
      // Если квадратик вылетел за пределы экрана
 | 
			
		||||
      if (box.y - box.halfSize > this.elem.height) {
 | 
			
		||||
        box.y -= this.elem.height + 100;
 | 
			
		||||
        this.updateBox(box);
 | 
			
		||||
      // 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.x - box.halfSize > this.elem.width) {
 | 
			
		||||
        box.x -= this.elem.width + 100;
 | 
			
		||||
      if (box.position.x - box.halfSize > this.elem.width) {
 | 
			
		||||
        box.position.x -= this.elem.width + 100;
 | 
			
		||||
        shouldUpdateBox = true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (shouldUpdateBox) {
 | 
			
		||||
        this.updateBox(box);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -143,14 +166,11 @@ export default class BoxesField {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {Box} box
 | 
			
		||||
   */
 | 
			
		||||
  updateBox(box) {
 | 
			
		||||
  updateBox(box: Box): void {
 | 
			
		||||
    box.color = this.getRandomColor();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getRandomColor() {
 | 
			
		||||
  getRandomColor(): string {
 | 
			
		||||
    return this.params.boxColors[
 | 
			
		||||
      Math.floor(Math.random() * this.params.boxColors.length)
 | 
			
		||||
    ];
 | 
			
		||||
							
								
								
									
										4
									
								
								packages/app/components/ui/bsod/Point.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/app/components/ui/bsod/Point.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
export default interface Point {
 | 
			
		||||
  x: number;
 | 
			
		||||
  y: number;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
export const BSOD = 'BSOD';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @returns {object}
 | 
			
		||||
 */
 | 
			
		||||
export function bsod() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: BSOD,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/app/components/ui/bsod/actions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/app/components/ui/bsod/actions.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { Action } from 'redux';
 | 
			
		||||
 | 
			
		||||
export const BSOD = 'BSOD';
 | 
			
		||||
 | 
			
		||||
export function bsod(): Action {
 | 
			
		||||
  return {
 | 
			
		||||
    type: BSOD,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ import { Store } from 'app/reducers';
 | 
			
		||||
import { History } from 'history';
 | 
			
		||||
 | 
			
		||||
import { bsod } from './actions';
 | 
			
		||||
import BSoD from './BSoD';
 | 
			
		||||
import BSoDContainer from './BSoDContainer';
 | 
			
		||||
 | 
			
		||||
let injectedStore: Store;
 | 
			
		||||
let injectedHistory: History<any>;
 | 
			
		||||
@@ -20,7 +20,7 @@ export default function dispatchBsod(
 | 
			
		||||
 | 
			
		||||
  ReactDOM.render(
 | 
			
		||||
    <ContextProvider store={store} history={history}>
 | 
			
		||||
      <BSoD />
 | 
			
		||||
      <BSoDContainer />
 | 
			
		||||
    </ContextProvider>,
 | 
			
		||||
    document.getElementById('app'),
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
@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%;
 | 
			
		||||
@@ -46,9 +45,16 @@ $font-family-monospaced: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Roboto Mono',
 | 
			
		||||
 | 
			
		||||
.support {
 | 
			
		||||
  font-size: 18px;
 | 
			
		||||
  line-height: 18px;
 | 
			
		||||
  color: #fff;
 | 
			
		||||
  margin: 3px 0 44px;
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin: 5px 0 44px;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  border-bottom-color: #39777f;
 | 
			
		||||
 | 
			
		||||
  &:hover {
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    border-bottom-color: #eee;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.easterEgg {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user