aboutsummaryrefslogtreecommitdiff
path: root/src/timer.ml
blob: 5ff0b8bc7183fea4b1e43207df15abaa05ef2ac8 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(* Audio *)

let c3 = Audio.create "sounds/c3.mp3"

let c4 = Audio.create "sounds/c4.mp3"

let c5 = Audio.create "sounds/c5.mp3"

let playAudio (step : Step.state) =
  match step.step with
  | Step.Prepare when step.remaining == !Config.config.prepare ->
      Audio.playOrReplay c3
  | Step.Work when step.remaining == !Config.config.work ->
      Audio.playOrReplay c5
  | Step.Rest when step.remaining == !Config.config.rest ->
      Audio.playOrReplay c3
  | Step.End -> Audio.playOrReplay c3
  | _ -> if step.remaining <= 3 then Audio.playOrReplay c4 else ()

(* Elements *)

let timerElt = Document.querySelectorUnsafe "#g-Timer"

let dialElt = Document.querySelectorUnsafe "#g-Timer__Dial"

let arcPathElt = Document.querySelectorUnsafe "#g-Timer__ArcProgress"

let stepElt = Document.querySelectorUnsafe "#g-Timer__Step"

let durationElt = Document.querySelectorUnsafe "#g-Timer__Duration"

let tabataCurrentElt = Document.querySelectorUnsafe "#g-Timer__TabataCurrent"

let tabataTotalElt = Document.querySelectorUnsafe "#g-Timer__TabataTotal"

let cycleCurrentElt = Document.querySelectorUnsafe "#g-Timer__CycleCurrent"

let cycleTotalElt = Document.querySelectorUnsafe "#g-Timer__CycleTotal"

let stopElt = Document.querySelectorUnsafe "#g-Timer__Stop"

(* State *)

let interval = ref None

let duration = ref 0

let elapsedTime = ref 0

let onStop : (unit -> unit) ref = ref (fun () -> ())

let isPlaying = ref false

(* Actions *)

let playPause _ = isPlaying := not !isPlaying

let stop _ =
  let () = Belt.Option.forEach !interval Js.Global.clearInterval in
  !onStop ()

(* View *)

let updateDom () =
  let angle =
    Js.Int.toFloat !elapsedTime /. Js.Int.toFloat !duration *. 360.0
  in
  let () =
    Element.setAttribute arcPathElt "d" (Arc.describe 0.0 0.0 95.0 0.0 angle)
  in
  let step = Step.getAt !Config.config !elapsedTime in
  let () = Element.setInnerText stepElt (Step.prettyPrint step.step) in
  let () =
    Element.setInnerText durationElt (Duration.prettyPrint step.remaining)
  in
  let () =
    Element.setInnerText tabataCurrentElt (Js.Int.toString step.tabata)
  in
  let () = playAudio step in
  Element.setInnerText cycleCurrentElt (Js.Int.toString step.cycle)

(* Update *)

let update () =
  if !isPlaying then
    let () = elapsedTime := !elapsedTime + 1 in
    if !elapsedTime > !duration then stop () else updateDom ()
  else ()

(* Init *)

let init () =
  let () = duration := Config.getDuration () in
  let () = elapsedTime := 0 in
  let () =
    Element.setInnerText tabataTotalElt (Js.Int.toString !Config.config.tabatas)
  in
  Element.setInnerText cycleTotalElt (Js.Int.toString !Config.config.cycles)

(* Setup and start *)

let setup onTimerStop = onStop := onTimerStop

let show () =
  let () = updateDom () in
  Element.setStyle timerElt "display: flex"

let hide () = Element.setStyle timerElt "display: none"

let start () =
  let () = interval := Some (Js.Global.setInterval update 1000) in
  isPlaying := true

let () =
  let () = Element.addEventListener stopElt "click" stop in
  Element.addEventListener dialElt "click" playPause