summaryrefslogtreecommitdiffhomepage
path: root/gui/src/main/tray-icon-controller.ts
blob: f3333a2636a1b838bc2ec82ae7bcd35d283211b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { nativeImage, NativeImage, Tray } from 'electron';
import path from 'path';
import KeyframeAnimation from './keyframe-animation';

export type TrayIconType = 'unsecured' | 'securing' | 'secured';

export default class TrayIconController {
  private animation?: KeyframeAnimation;
  private iconImages: NativeImage[] = [];
  private monochromaticIconImages: NativeImage[] = [];

  constructor(
    tray: Tray,
    private iconTypeValue: TrayIconType,
    private useMonochromaticIconValue: boolean,
  ) {
    this.loadImages();

    const initialFrame = this.targetFrame();
    const animation = new KeyframeAnimation();
    animation.speed = 100;
    animation.onFrame = (frameNumber) => tray.setImage(this.imageForFrame(frameNumber));
    animation.play({ start: initialFrame, end: initialFrame });

    this.animation = animation;
  }

  public dispose() {
    if (this.animation) {
      this.animation.stop();
      this.animation = undefined;
    }
  }

  get iconType(): TrayIconType {
    return this.iconTypeValue;
  }

  set useMonochromaticIcon(useMonochromaticIcon: boolean) {
    this.useMonochromaticIconValue = useMonochromaticIcon;

    if (this.animation && !this.animation.isRunning) {
      this.animation.play({ end: this.targetFrame() });
    }
  }

  public animateToIcon(type: TrayIconType) {
    if (this.iconTypeValue === type || !this.animation) {
      return;
    }

    this.iconTypeValue = type;

    const animation = this.animation;
    const frame = this.targetFrame();

    animation.play({ end: frame });
  }

  private loadImages() {
    const basePath = path.resolve(path.join(__dirname, '../../assets/images/menubar icons'));
    const frames = Array.from({ length: 10 }, (_, i) => i + 1);

    this.iconImages = frames.map((frame) =>
      nativeImage.createFromPath(path.join(basePath, `lock-${frame}.png`)),
    );

    this.monochromaticIconImages = frames.map((frame) =>
      nativeImage.createFromPath(path.join(basePath, `lock-${frame}Template.png`)),
    );
  }

  private targetFrame(): number {
    switch (this.iconTypeValue) {
      case 'unsecured':
        return 0;
      case 'securing':
        return 9;
      case 'secured':
        return 8;
    }
  }

  private imageForFrame(frame: number): NativeImage {
    return this.useMonochromaticIconValue
      ? this.monochromaticIconImages[frame]
      : this.iconImages[frame];
  }
}