Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A well-organized system #3

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 9 additions & 174 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,180 +1,15 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<title>Pomodoro</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>
<script src="p5.sound.js"></script>
<script>
var pomodoromode=false;
var modes=["60x60","25x25","45x10","1x1","0.1x1","90x90"];//add modes here
var buttons=[];
var work,rest;
var working=true;//false=resting
var currentButtonsAmount=0;
var buttonWidth,buttonHeight;//global
var startTime;
var endTime;
var doOnce=false;//for pomodoro screen
var doOnce2=false;//for start screen
var theText;//for pomodoro screen - global
var timeMode=1;//0=1hours,60minutes,3600seconds / 1=1hours,0minutes,0seconds
var timeLeft;
var bg;//global bg color
var _reset=false;
var soundEffect;
function setup() {
buttonWidth=windowWidth/5;
buttonHeight=windowHeight/10;

createCanvas(windowWidth, windowHeight);

soundEffect = loadSound('sound effect.mp3');

{//setup screen
background(200);

textAlign(CENTER);

var buttonsAmount=modes.length;
//create starting buttons
for(var i=0;i<buttonsAmount;i++){
buttons[i] = createButton(modes[i]);
buttons[i].position(windowWidth/2-buttonWidth/2, (windowHeight/2)+(i*buttonHeight)-(buttonsAmount*buttonHeight/2));
buttons[i].size(buttonWidth,buttonHeight);
buttons[i].mousePressed((function(index){
return function(){
setMode(modes[index]);
}
})(i));//IIFE
}
}
}
function draw() {
if(_reset){//executes once to reset program
//console.log("asldkas");
buttons.forEach((b)=>{b.remove()});
pomodoromode=false;
timeMode=1;
clear();
{//setup screen
background(200);

textAlign(CENTER);

var buttonsAmount=modes.length;
//create starting buttons
for(var i=0;i<buttonsAmount;i++){
buttons[i] = createButton(modes[i]);
buttons[i].position(windowWidth/2-buttonWidth/2, (windowHeight/2)+(i*buttonHeight)-(buttonsAmount*buttonHeight/2));
buttons[i].size(buttonWidth,buttonHeight);
buttons[i].mousePressed((function(index){
return function(){
setMode(modes[index]);
}
})(i));//IIFE
}
}

_reset=false;
}
if(bg=="grey"){
background(color(30));
}
if(pomodoromode){
pomodoro(work,rest);
}else{

}

}
function mouseClicked(){
if(pomodoromode){
timeMode=!timeMode;
}
}
function setMode(_mode){
m=1;
work=_mode.substring(0,_mode.indexOf("x"));
rest=_mode.substring(_mode.indexOf("x") + 1);
//console.log(work+"+"+rest);

buttons.forEach((b)=>{b.remove()});

currentButtonsAmount=1;
buttons[0]=makeButton("start",1,()=>{doOnce=true;pomodoromode=true});
}
function pomodoro(_work,_rest){//int,int
textSize((windowWidth+windowHeight)/2/25);

if(doOnce){
buttons.forEach((b)=>{b.remove()});
startTime=getTime();
endTime=getTargetTime((working==true)?_work:_rest);
currentButtonsAmount=6;
buttons[0]=makeButton("Reset",6,()=>{_reset=true;});

soundEffect.play();

doOnce=false;
}
background((working==true)?color(0,255,0):color(0,0,255));

timeLeft=getTimeDifference(millis()/1000,endTime);
if(timeLeft[2]<=0){//negative seconds left
working=!working;//switch modes
doOnce=true;//recalculate time to finish
}
if(timeMode==0){//set text based on display mode
theText=text(((working==true)?"Working":"Resting")+" for "+((working==true)?_work:_rest)+" minutes. \n"
+timeLeft[0]+" hours remaining. \n"
+timeLeft[1]+" minutes remaining. \n"
+timeLeft[2]+" seconds remaining. \n"
,windowWidth/2,windowHeight/2);
}else{//=1
var secondsfixed=(timeLeft[2]>=60)?timeLeft[2]%60:timeLeft[2];
secondsfixed=parseFloat(secondsfixed).toFixed(2);
theText=text(((working==true)?"Working":"Resting")+" for "+((working==true)?_work:_rest)+" minutes. \n"
+timeLeft[0]+" hours and\n"
+((timeLeft[1]>=60)?timeLeft[1]%60:timeLeft[1])+" minutes and\n"
+secondsfixed+" seconds remaining. \n"
,windowWidth/2,windowHeight/2);
}

}
function makeButton(txt,totalButtons,mPressed){//string,int,function
//totalButtons=intended total number of buttons
//currentButtonsAmount=the current number of buttons on screen before creation of this one
var tmp;
tmp = createButton(txt);
tmp.position(windowWidth/2-buttonWidth/2, (windowHeight/2)+(currentButtonsAmount*buttonHeight)-(totalButtons*buttonHeight/2));
tmp.size(buttonWidth,buttonHeight);
tmp.mousePressed(mPressed);//function

currentButtonsAmount++;
return tmp;
}
function getTime(){//get time in seconds since start of program
return millis()/1000;
}
function getTargetTime(mins){//mins=minutes in the future
return millis()/1000+(mins*60);//returns time in seconds from setup() to target time
}
function getTimeDifference(current,target){//params are seconds
var temp=[];//0=hours,1=minutes,2=seconds
var diff=(target-current)//difference in seconds
temp[0] = Math.floor(diff/60/60);//hours
temp[1] = Math.floor(diff/60);//minutes
temp[2] = diff;//seconds
temp[2] = temp[2].toFixed(2);//seconds to two decimal places

return temp;
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}

</script>
<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>
<script src="static/js/p5.sound.js"></script>
<script type="module" src="static/js/main.js"></script>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separation of concerns - I called this one "Code-Interface Segregation".

</body>
</html>

</html>
File renamed without changes.
168 changes: 168 additions & 0 deletions static/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Due to the way processing works, some variables that are treated as constants can't be declared as such, because they must be set in `setup()`.
// We could probably avert this issue by severing the processing library from this project, and rewriting it in terms of DOM interaction.

import { timeFormatter } from "./presets.js";
import { getTargetTime, getTime, getTimeDifference } from "./timeUtils.js";
import TimeInformation from "./timeInformation.js";

const modes = ["60x60", "25x25", "45x10", "1x1", "0.1x1", "0.02x0.02", "90x90"]; //add modes here

var timerRunning = false;
var buttons = [];
var currentWorkTime, currentRestTime;
var working = true; //false=resting
var currentButtonsAmount = 0;
var buttonWidth, buttonHeight; // Should be constant, but due to the way processing works, must be set in `setup()`.
var endTime;
var resetTimer = false; //for pomodoro screen
var theText; //for pomodoro screen - global
var timeLeft;
var soundEffect; // Should be constant.
Comment on lines +10 to +20
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these should be able to be turned into let without unexpected behavior. Ideally we would remove as many variables as possible.


function setup() {
buttonWidth = windowWidth / 5;
buttonHeight = windowHeight / 10;

createCanvas(windowWidth, windowHeight);

soundEffect = loadSound("static/audio/sound effect.mp3");

background(200);
textAlign(CENTER);
buttons = makeButtonsFromModes(modes);
}

function draw() {
if (timerRunning) pomodoro(currentWorkTime, currentRestTime);
}

function resetProgram() {
//executes once to reset program
buttons.forEach((b) => b.remove());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do see this call repeated in a few places. It's probably wise to extract this into its own function.

timerRunning = false;
clear();
//setup screen
background(200);

textAlign(CENTER);

buttons = makeButtonsFromModes(modes);
}

function mouseClicked() {
if (timerRunning) timeFormatter.next();
}

/**
* Makes one button to start the timer with the specified mode and destroys all other buttons.
* @param {string} mode - The timer mode, formatted like so: `"<Work time>x<Rest time>"`
*/
function setMode(mode) {
currentWorkTime = mode.substring(0, mode.indexOf("x"));
currentRestTime = mode.substring(mode.indexOf("x") + 1);

buttons.forEach((b) => b.remove());

buttons = [
makeButton(
"start",
(currentButtonsAmount = 1),
() => (resetTimer = timerRunning = true)
),
];
}

/**
* Updates the screen to show the proper text about time left in a session.
* @param {number} workTime - The time to spend working
* @param {number} restTime - The time to spend resting
*/
function pomodoro(workTime, restTime) {
textSize((windowWidth + windowHeight) / 2 / 25);

if (resetTimer) {
buttons.forEach((b) => b.remove());
endTime = getTargetTime(working ? workTime : restTime);
buttons = [
makeButton(
"Reset",
(currentButtonsAmount = modes.length),
resetProgram
),
];

soundEffect.play();

resetTimer = false;
}
Comment on lines +83 to +97
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be better, we should strive for separation of concerns.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be ideal to make a function call instead of setting a global variable used in another function, maybe making a resetTimer function would do the job.


background(working ? color(0, 255, 0) : color(64, 64, 255));

timeLeft = getTimeDifference(getTime(), endTime);

if (timeLeft.seconds <= 0) {
working = !working;
resetTimer = true; //recalculate time to finish
}

const currentTimeInfo = new TimeInformation(
working,
workTime,
restTime,
timeLeft
);

theText = standardText(timeFormatter.textFrom(currentTimeInfo));
Comment on lines +108 to +115
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section of code now adheres to the Open-Closed Principle.

}

function standardText(txt) {
return text(txt, windowWidth / 2, windowHeight / 2);
}

/**
* Creates a list of buttons from a list of timer modes. Each button will start the corresponding mode when clicked.
* @param {string[]} modes - List of timer modes that the buttons should start.
* @returns {Button[]} The list of buttons that start the timer with their mode.
*/
function makeButtonsFromModes(modes) {
return modes
.map(createButton)
.map((b, idx) =>
b.position(
windowWidth / 2 - buttonWidth / 2,
windowHeight / 2 +
idx * buttonHeight -
(modes.length * buttonHeight) / 2
Comment on lines +132 to +135
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is complex logic that would be good to describe in more detail.

)
)
.map((b) => b.size(buttonWidth, buttonHeight))
.map((b, idx) => b.mousePressed(() => setMode(modes[idx])));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be the best solution, but at least we depend only on a parameter.

}

/**
* Creates a button on the screen.
* @param {string} txt - The text on the button
* @param {number} totalButtons - The intended total number of buttons
* @param {() => void} mPressed - The callback to be ran when the button is clicked
* @returns
*/
function makeButton(txt, totalButtons, mPressed) {
//currentButtonsAmount=the current number of buttons on screen before creation of this one
var button = createButton(txt);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be let. Would be even better as const but we do need to modify it. There is probably some way to express these modifying method calls in a chain so the output can be const. Maybe a monad?

button.position(
windowWidth / 2 - buttonWidth / 2,
windowHeight / 2 +
currentButtonsAmount * buttonHeight -
(totalButtons * buttonHeight) / 2
);
Comment on lines +152 to +157
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic looks to be duplicated in makeButtonsFromModes. Needless to say, this is not ideal.

button.size(buttonWidth, buttonHeight);
button.mousePressed(mPressed);

currentButtonsAmount++;

return button;
}

window.setup = setup;
window.mouseClicked = mouseClicked;
window.draw = draw;
Comment on lines +166 to +168
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small trick for p5.js to "see" the functions we've declared when using the script with type="module".

File renamed without changes.
7 changes: 7 additions & 0 deletions static/js/presets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import TimeFormatter from "./timeFormatter.js";
import * as timeModes from "./timeModes.js";

export const timeFormatter = new TimeFormatter([
timeModes.dependent,
timeModes.independent,
]);
Loading