summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@codeispoetry.ru>2017-06-06 19:02:04 +0300
committerAndrej Mihajlov <and@codeispoetry.ru>2017-06-08 22:39:19 +0300
commit891eb4cab0732b325a27dc104d942a052c82a930 (patch)
treedb541fc036c8309efd79d2f1a86675a482ad45aa
parent3f9e67cf0e1793d404b29b85ceaa49c281aba591 (diff)
downloadmullvadvpn-891eb4cab0732b325a27dc104d942a052c82a930.tar.xz
mullvadvpn-891eb4cab0732b325a27dc104d942a052c82a930.zip
Add flow annotations for KeyframeAnimation and relevant Electron types
-rw-r--r--.flowconfig1
-rw-r--r--app/lib/keyframe-animation.js189
-rw-r--r--app/lib/tray-icon-manager.js4
-rw-r--r--flow-libs/electron.js.flow28
4 files changed, 150 insertions, 72 deletions
diff --git a/.flowconfig b/.flowconfig
index 61b80b68a9..6d8e2c3c6e 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -7,6 +7,7 @@
[include]
[libs]
+flow-libs/
[options]
munge_underscores=true
diff --git a/app/lib/keyframe-animation.js b/app/lib/keyframe-animation.js
index 10b087e109..4890d06a0e 100644
--- a/app/lib/keyframe-animation.js
+++ b/app/lib/keyframe-animation.js
@@ -1,5 +1,16 @@
+// @flow
import assert from 'assert';
import { nativeImage } from 'electron';
+import type { NativeImage } from 'electron';
+
+export type OnFrameFn = (image: NativeImage) => void;
+export type OnFinishFn = (void) => void;
+export type KeyframeAnimationOptions = {
+ startFrame?: number,
+ endFrame?: number,
+ beginFromCurrentState?: boolean,
+ advanceTo?: 'end'
+};
/**
* Keyframe animation
@@ -9,13 +20,32 @@ import { nativeImage } from 'electron';
*/
export default class KeyframeAnimation {
+ _speed: number = 200; // ms
+ _repeat: boolean = false;
+ _reverse: boolean = false;
+ _alternate: boolean = false;
+
+ _onFrame: ?OnFrameFn;
+ _onFinish: ?OnFinishFn;
+
+ _nativeImages: Array<NativeImage>;
+ _frameRange: Array<number>;
+ _numFrames: number;
+ _currentFrame: number = 0;
+
+ _isRunning: boolean = false;
+ _isFinished: boolean = false;
+ _isFirstRun: boolean = true;
+
+ _timeout = null;
+
/**
* Set callback called on each frame update
*
* @type {function}
* @memberOf KeyframeAnimation
*/
- set onFrame(v) { this._onFrame = v; }
+ set onFrame(newValue: ?OnFrameFn) { this._onFrame = newValue; }
/**
* Get callback called on each frame update
@@ -24,7 +54,7 @@ export default class KeyframeAnimation {
* @type {function}
* @memberOf KeyframeAnimation
*/
- get onFrame() { this._onFrame; }
+ get onFrame(): ?OnFrameFn { this._onFrame; }
/**
* Set callback called when animation finished
@@ -32,7 +62,7 @@ export default class KeyframeAnimation {
* @type {function}
* @memberOf KeyframeAnimation
*/
- set onFinish(v) { this._onFinish = v; }
+ set onFinish(newValue: ?OnFinishFn) { this._onFinish = newValue; }
/**
* Get callback called when animation finished
@@ -41,7 +71,7 @@ export default class KeyframeAnimation {
*
* @memberOf KeyframeAnimation
*/
- get onFinish() { this._onFinish; }
+ get onFinish(): ?OnFinishFn { this._onFinish; }
/**
* Set animation pace per frame in ms
@@ -49,7 +79,7 @@ export default class KeyframeAnimation {
* @type {number}
* @memberOf KeyframeAnimation
*/
- set speed(v) { this._speed = parseInt(v); }
+ set speed(newValue: number) { this._speed = parseInt(newValue); }
/**
* Get animation pace per frame in ms
@@ -58,7 +88,7 @@ export default class KeyframeAnimation {
* @type {number}
* @memberOf KeyframeAnimation
*/
- get speed() { return this._speed; }
+ get speed(): number { return this._speed; }
/**
* Set animation repetition
@@ -66,7 +96,7 @@ export default class KeyframeAnimation {
*
* @memberOf KeyframeAnimation
*/
- set repeat(v) { this._repeat = !!v; }
+ set repeat(newValue: boolean) { this._repeat = !!newValue; }
/**
* Get animation repetition
@@ -75,14 +105,14 @@ export default class KeyframeAnimation {
* @type {bool}
* @memberOf KeyframeAnimation
*/
- get repeat() { return this._repeat; }
+ get repeat(): boolean { return this._repeat; }
/**
* Set animation reversal
* @type {bool}
* @memberOf KeyframeAnimation
*/
- set reverse(v) { this._reverse = !!v; }
+ set reverse(newValue: boolean) { this._reverse = !!newValue; }
/**
* Get animation reversal
@@ -91,14 +121,14 @@ export default class KeyframeAnimation {
* @type {bool}
* @memberOf KeyframeAnimation
*/
- get reverse() { return this._repeat; }
+ get reverse(): boolean { return this._repeat; }
/**
* Set animation alternation
* @type {bool}
* @memberOf KeyframeAnimation
*/
- set alternate(v) { this._alternate = !!v; }
+ set alternate(newValue: boolean) { this._alternate = !!newValue; }
/**
* Get animation alternation
@@ -107,25 +137,16 @@ export default class KeyframeAnimation {
* @type {bool}
* @memberOf KeyframeAnimation
*/
- get alternate() { return this._alternate; }
-
- /**
- * Source array of images
- *
- * @readonly
- * @type {array}
- * @memberOf KeyframeAnimation
- */
- get source() { return this._source.slice(); }
+ get alternate(): boolean { return this._alternate; }
/**
* Array of NativeImage instances loaded based on source input
*
* @readonly
- * @type {Electron.NativeImage[]}
+ * @type {Array<NativeImage>}
* @memberOf KeyframeAnimation
*/
- get nativeImages() { return this._nativeImages.slice(); }
+ get nativeImages(): Array<NativeImage> { return this._nativeImages.slice(); }
/**
* Flag that tells whether animation finished
@@ -134,83 +155,81 @@ export default class KeyframeAnimation {
* @type {bool}
* @memberOf KeyframeAnimation
*/
- get isFinished() { return this._isFinished; }
+ get isFinished(): boolean { return this._isFinished; }
/**
- * Create animation using file sequence
+ * Create animation from files matching filename pattern
*
* @static
- * @param {string} filePattern - file name pattern where {s} is replaced with index
- * @param {number[]} range - sequence range [start, end]
+ * @param {string} filePattern - file name pattern where {} is replaced with index
+ * @param {Array<number>} range - sequence range [start, end]
*
* @memberOf KeyframeAnimation
* @return {KeyframeAnimation}
*/
- static fromFileSequence(filePattern, range) {
+ static fromFilePattern(filePattern: string, range: Array<number>): KeyframeAnimation {
assert(range.length === 2 && range[0] < range[1], 'the animation range is invalid');
-
- let images = [];
+ const images: Array<NativeImage> = [];
for(let i = range[0]; i <= range[1]; i++) {
- images.push(filePattern.replace('{s}', i));
+ const filePath = filePattern.replace('{}', i.toString());
+ const image = nativeImage.createFromPath(filePath);
+ images.push(image);
}
+ return new KeyframeAnimation(images);
+ }
+ /**
+ * Create animation from file sequence
+ *
+ * @static
+ * @param {Array<string>} files - file paths
+ * @returns {KeyframeAnimation}
+ *
+ * @memberof KeyframeAnimation
+ */
+ static fromFileSequence(files: Array<string>): KeyframeAnimation {
+ const images: Array<NativeImage> = files.map(filePath => nativeImage.createFromPath(filePath));
return new KeyframeAnimation(images);
}
/**
- * Creates an instance of KeyframeAnimation.
- * @param {string[]} images
+ * Create an instance of KeyframeAnimation
+ * @param {Array<NativeImage>} images - an array of instances of NativeImage
*
* @memberOf KeyframeAnimation
*/
- constructor(images) {
- assert(images.length > 0, 'too few images in animation');
-
- this._source = images.slice();
- this._nativeImages = images.map((pathOrNativeImage) => {
- if(typeof(pathOrNativeImage) === 'string') {
- return nativeImage.createFromPath(pathOrNativeImage);
- } else if((pathOrNativeImage + '') === '[object NativeImage]') {
- return pathOrNativeImage;
- }
- return nativeImage.createEmpty();
- });
-
- this._speed = 200; // ms
- this._repeat = false;
- this._reverse = false;
- this._alternate = false;
+ constructor(images: Array<NativeImage>) {
+ const len = images.length;
- this._numFrames = images.length;
- this._currentFrame = 0;
- this._frameRange = [0, this._numFrames];
- this._isRunning = false;
- this._isFinished = false;
+ assert(len > 0, 'too few images in animation');
- this._isFirstRun = true;
+ this._nativeImages = images.slice();
+ this._numFrames = len;
+ this._frameRange = [0, len];
}
/**
* Get current sprite
*
* @readonly
- * @type {Electron.NativeImage}
+ * @type {NativeImage}
* @memberOf KeyframeAnimation
*/
- get currentImage() {
+ get currentImage(): NativeImage {
return this._nativeImages[this._currentFrame];
}
/**
- * Prepare initial state for animation before running it.
- * @param {object} [options = {}] - animation options
- * @param {number} [options.startFrame] - start frame
- * @param {number} [options.endFrame] - end frame
- * @param {bool} [options.beginFromCurrentState] - continue animation from current state
- * @param {string} [options.advanceTo] - resets current frame. (possible values: end)
+ * Start animation
+ *
+ * @param {object} [options = {}] - animation options
+ * @param {number} [options.startFrame] - start frame
+ * @param {number} [options.endFrame] - end frame
+ * @param {bool} [options.beginFromCurrentState] - continue animation from current state
+ * @param {string} [options.advanceTo] - resets current frame. (possible values: end)
* @memberOf KeyframeAnimation
*/
- play(options = {}) {
+ play(options: KeyframeAnimationOptions = {}) {
let { startFrame, endFrame, beginFromCurrentState, advanceTo } = options;
if(startFrame !== undefined && endFrame !== undefined) {
@@ -256,6 +275,12 @@ export default class KeyframeAnimation {
this._unscheduleUpdate();
}
+ /**
+ * Cancel timer for next animation frame
+ *
+ * @private
+ * @memberof KeyframeAnimation
+ */
_unscheduleUpdate() {
if(this._timeout) {
clearTimeout(this._timeout);
@@ -263,16 +288,34 @@ export default class KeyframeAnimation {
}
}
+ /**
+ * Schedule timer for next animation frame
+ *
+ * @private
+ * @memberof KeyframeAnimation
+ */
_scheduleUpdate() {
- this._timeout = setTimeout(::this._onUpdateFrame, this._speed);
+ this._timeout = setTimeout(() => this._onUpdateFrame(), this._speed);
}
+ /**
+ * Call delegate to render frame
+ *
+ * @private
+ * @memberof KeyframeAnimation
+ */
_render() {
if(this._onFrame) {
this._onFrame(this._nativeImages[this._currentFrame]);
}
}
+ /**
+ * Mark animation finished and notify delegate.
+ *
+ * @private
+ * @memberof KeyframeAnimation
+ */
_didFinish() {
this._isFinished = true;
@@ -281,6 +324,12 @@ export default class KeyframeAnimation {
}
}
+ /**
+ * Animation frame lifecycle.
+ *
+ * @private
+ * @memberof KeyframeAnimation
+ */
_onUpdateFrame() {
this._advanceFrame();
@@ -332,14 +381,14 @@ export default class KeyframeAnimation {
/**
* Calculate next frame
* @private
- * @param {number} cur - current frame
- * @param {number[]} frameRange - frame range
- * @param {bool} isReverse - reverse sequence direction?
+ * @param {number} cur - current frame
+ * @param {Array<number>} frameRange - frame range
+ * @param {bool} isReverse - reverse sequence direction?
* @returns {number}
*
* @memberOf KeyframeAnimation
*/
- _nextFrame(cur, frameRange, isReverse) {
+ _nextFrame(cur: number, frameRange: Array<number>, isReverse: boolean): number {
if(isReverse) {
if(cur < frameRange[0]) {
return cur + 1;
diff --git a/app/lib/tray-icon-manager.js b/app/lib/tray-icon-manager.js
index 8898f7bc48..eb242deb6b 100644
--- a/app/lib/tray-icon-manager.js
+++ b/app/lib/tray-icon-manager.js
@@ -21,8 +21,8 @@ export default class TrayIconManager {
assert(tray, 'Tray icon cannot be null');
const basePath = path.join(path.resolve(__dirname, '..'), 'assets/images/menubar icons');
- let filePath = path.join(basePath, 'lock-{s}.png');
- let animation = KeyframeAnimation.fromFileSequence(filePath, [1, 9]);
+ let filePath = path.join(basePath, 'lock-{}.png');
+ let animation = KeyframeAnimation.fromFilePattern(filePath, [1, 9]);
animation.onFrame = (img) => tray.setImage(img);
animation.speed = 100;
diff --git a/flow-libs/electron.js.flow b/flow-libs/electron.js.flow
new file mode 100644
index 0000000000..da5219e3a3
--- /dev/null
+++ b/flow-libs/electron.js.flow
@@ -0,0 +1,28 @@
+/**
+ * Flow annotations for Electron
+ * @flow
+ */
+
+declare module 'electron' {
+
+ // common types
+
+ declare type Size = {
+ width: number;
+ height: number;
+ }
+
+ // https://github.com/electron/electron/blob/master/docs/api/native-image.md
+
+ declare class NativeImage {
+ isEmpty(): boolean;
+ getSize(): Size;
+ }
+
+ declare var nativeImage: {
+ createEmpty(): NativeImage,
+ createFromPath(path: string): NativeImage,
+ createFromBuffer(buffer: Buffer, scaleFactor?: number): NativeImage,
+ createFromDataURL(dataURL: string): NativeImage,
+ }
+} \ No newline at end of file