diff --git a/apps/rep/app.ts b/apps/rep/app.ts index 635baf378..fa6337153 100644 --- a/apps/rep/app.ts +++ b/apps/rep/app.ts @@ -18,59 +18,44 @@ const reps: Rep[] = [ // 3 min static recovery // 1 X 4 mins to finish - {dur:1/60, label:"1st-sec"}, + {dur:3/60, label:"1st-sec"}, {dur:5/60, label:"5-sec"}, - {dur:4, label:"jog"}, - {dur:4, label:"recovery"}, + // {dur:4, label:"jog"}, + // {dur:4, label:"recovery"}, - {dur:2, label:"jog"}, - {dur:2, label:"recovery"}, - {dur:2, label:"jog"}, - {dur:2, label:"recovery"}, + // {dur:2, label:"jog"}, + // {dur:2, label:"recovery"}, + // {dur:2, label:"jog"}, + // {dur:2, label:"recovery"}, - {dur:1, label:"jog"}, - {dur:1, label:"recovery"}, - {dur:1, label:"jog"}, - {dur:1, label:"recovery"}, - {dur:1, label:"jog"}, - {dur:1, label:"recovery"}, - {dur:1, label:"jog"}, - {dur:1, label:"recovery"}, + // {dur:1, label:"jog"}, + // {dur:1, label:"recovery"}, + // {dur:1, label:"jog"}, + // {dur:1, label:"recovery"}, + // {dur:1, label:"jog"}, + // {dur:1, label:"recovery"}, + // {dur:1, label:"jog"}, + // {dur:1, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, + // {dur:0.5, label:"jog"}, {dur:0.5, label:"recovery"}, - {dur:3, label:"static recovery"}, - {dur:4, label:"finish"}, + // {dur:3, label:"static recovery"}, + // {dur:4, label:"finish"}, ]; const fontSzMain = 64; const fontSzRep = 20; +const fontSzRepDesc = 12; -// FIXME: `Layout_` name -const repLayout = (id: string): (Layout_.Hierarchy & {type:"txt"})[] => [ - { - id: `${id}_name`, - type: "txt", - font: `Vector:${fontSzRep}`, - label: "Name PH", - //pad: 4, - fillx: 1, - }, { - id: `${id}_dur`, - type: "txt", - font: `Vector:${fontSzRep}`, - label: "DURATION", - halign: Layout_.Align.Right, - } -]; +// FIXME: `Layout_` name, e.g. Layout_.Align.Right // top: show current rep: name & duration // middle: show time left on current rep @@ -80,53 +65,159 @@ const layout = new Layout({ type: "v", c: [ { - id: "cur_time", - type: "txt", + id: "duration", + lazyBuster: 1, + type: "custom", font: `Vector:${fontSzMain}` as FontNameWithScaleFactor, // modified in draw - label: "MM:SS", // TODO: empty strings fillx: 1, filly: 1, + render: (l: Layout_.RenderedHierarchy) => { + let lbl; + + g.clearRect(l.x, l.y, l.x+l.w, l.y+l.h); + + if(state){ + const elapsed = getElapsed(state); + const i = currentRepIndex(elapsed); + + if(i == null){ + // FIXME: dodgy end-of-rep handling + lbl = msToHM(elapsed); + }else{ + const thisDur = repDuration(reps[i]!); + const remaining = thisDur - elapsed; + lbl = msToHM(remaining); + + const fract = elapsed / thisDur; + g.setColor("#00f") + .fillRect( + l.x, + l.y, + l.x + fract * l.w, + l.y+l.h + ); + } + }else{ + lbl = "RDY"; + } + + g.setColor(l.col || g.theme.fg) + .setFont(l.font!) + .setFontAlign(0, 0) + .drawString( + lbl, + l.x+(l.w>>1), + l.y+(l.h>>1) + ); + }, + }, + { + type: "txt", + font: `Vector:${fontSzRepDesc}`, + label: "Activity / Duration", + }, + { + id: `cur_name`, + type: "txt", + font: `Vector:${fontSzRep}`, + label: "", + //pad: 4, + fillx: 1, + }, + { + type: "txt", + font: `Vector:${fontSzRepDesc}`, + label: "Next / Duration", + }, + { + id: `next_name`, + type: "txt", + font: `Vector:${fontSzRep}`, + label: "", + //pad: 4, + fillx: 1, }, { type: "h", - c: repLayout("cur"), - filly: 1, - }, - { - type: "h", - c: repLayout("next"), - filly: 1, - }, - { - type: "h", - c: [{ - id: "play", - type: "btn", - label: "Play", // TODO: change - fillx: 1, - cb: () => { - console.log("cb()"); + c: [ + { + id: "prev", + type: "btn", + label: "<", + fillx: 1, + cb: () => onPrev(), }, - filly: 1, - }] + { + id: "play", + type: "btn", + label: "Play", // TODO: change + fillx: 1, + cb: () => onToggle(), + }, + { + id: "next", + type: "btn", + label: ">", + fillx: 1, + cb: () => onNext(), + } + ] } ] }, {lazy: true}); -let index = 0; -let begin: number | undefined = Date.now(); // TODO: start null -let drawTimeout: number | undefined; -let paused = false; -let positioned = false; +const onToggle = () => { + if(!state){ + state = { + begin: Date.now(), + accumulated: 0, + }; + play(true, state); + }else{ + play(paused, state); + } -const pad2 = (s: number) => ('0' + s.toFixed(0)).slice(-2); + drawRep(); +}; + +const onPrev = () => { +}; + +const onNext = () => { +}; + +const play = (p: boolean, state: State) => { + if(p){ + layout["play"]!.label = "Pause"; + + state.begin = Date.now(); + + drawInterval = setInterval(drawRep, 1000); + }else{ + layout["play"]!.label = "Play"; + + const diff = Date.now() - state.begin; + state.accumulated += diff; + + clearInterval(drawInterval!); + } + + paused = !p; +} + +type State = { begin: number, accumulated: number }; + +let state: State | undefined; +//let drawTimeout: number | undefined; +let paused = false; // TODO: ditch this, used drawInterval +let drawInterval: IntervalId | undefined; const currentRepIndex = (elapsedMs: number) => { let total = 0; let ent; for(let i = 0; ent = reps[i]; i++){ - total += ent.dur * 60 * 1000; - if(elapsedMs < total) + total += repDuration(ent); + if(elapsedMs <= total) return i; } return null; @@ -135,82 +226,64 @@ const currentRepIndex = (elapsedMs: number) => { const repToLabel = (i: number, id: string) => { const rep = reps[i]; if(rep){ - layout[`${id}_name`]!.label = `${i+1}: ${rep.label}`; + layout[`${id}_name`]!.label = `${i+1}: ${rep.label} / ${rep.dur.toFixed(0)}m`; // FIXME: display time, i.e. hh:mm - layout[`${id}_dur`]!.label = `${rep.dur.toFixed(0)}m`; + //layout[`${id}_dur`]!.label = ``; }else{ emptyLabel(id); } }; const emptyLabel = (id: string) => { - layout[`${id}_name`]!.label = ""; - layout[`${id}_dur`]!.label = `0m`; + layout[`${id}_name`]!.label = " / 0m"; +}; + +const pad2 = (s: number) => ('0' + s.toFixed(0)).slice(-2); + +const repDuration = (rep: Rep) => rep.dur * 60 * 1000; + +const getElapsed = (state: State) => + Date.now() - state.begin + state.accumulated; + +const msToHM = (ms: number) => { + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + return min.toFixed(0) + ":" + pad2(sec % 60); }; const drawRep = () => { - if(paused || !begin){ // TODO: separate case for paused - layout.clear(); - layout["cur_time"]!.label = "Ready"; + (layout["duration"] as any).lazyBuster ^= 1; - if(!positioned){ - positioned = true; - layout.update(); // position + if(state){ + // TODO: layout.clear(layout.next_name); layout.render(layout.next_name) + + const elapsed = getElapsed(state); + const i = currentRepIndex(elapsed); + if(i !== null){ + repToLabel(i, "cur"); + repToLabel(i+1, "next"); + }else{ + emptyLabel("cur"); + emptyLabel("next"); } - - layout.render(layout["play"]); - layout.render(layout["cur_time"]); - return; - } - - // TODO: layout.clear(layout.next_name); layout.render(layout.next_name) - - const now = Date.now(); - - const elapsed = now - begin; - const sec = Math.round(elapsed / 1000); - const min = Math.round(sec / 60); - const timeStr = min.toFixed(0) + ":" + pad2(sec % 60); - layout["cur_time"]!.label = timeStr; - - const i = currentRepIndex(elapsed); - if(i !== null){ - repToLabel(i, "cur"); - repToLabel(i+1, "next"); - }else{ - emptyLabel("cur"); - emptyLabel("next"); - } - - if(!positioned){ - positioned = true; - layout.update(); // position } layout.render(); - /* - g.reset() - .clearRect(Bangle.appRect) - .setFontAlign(0, 0) - .setFont("Vector", 55) - .drawString(timeStr, x, y) - .setFont("Vector", 33) - .drawString(current ? current.label + " for " + current.dur : "", x, y+48); - // TODO: figure out next rep change time? or every 5s, then countdown from 10-->0 if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function() { drawTimeout = undefined; drawRep(); }, 1000 - (Date.now() % 1000)); - */ }; g.clear(); -var drawInterval = setInterval(drawRep, 1000); drawRep(); +// TODO: swipe handlers +//Bangle.setUI + // TODO: widgets