Skip to content

Commit c8b2afa

Browse files
Feat/boilerplate pr (#3)
* feat/boilerplate-code * refactor(structure): make scripts into one file
1 parent 2d81367 commit c8b2afa

3 files changed

Lines changed: 113 additions & 17 deletions

File tree

src/js/main.js

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,85 @@
1-
import {
2-
createPomodoroState,
3-
switchMode,
4-
tickTimer,
5-
getDurationForMode,
6-
formatTime,
7-
} from './core/timer.js';
8-
import { updateModeButtons } from './ui/render.js';
1+
// Centralized timer durations (seconds) for each mode.
2+
export const DURATIONS = {
3+
focus: 25 * 60,
4+
'short-break': 5 * 60,
5+
'long-break': 15 * 60,
6+
};
7+
8+
// ===== Core timer helpers =====
9+
export function createPomodoroState() {
10+
return {
11+
mode: 'focus',
12+
remainingSeconds: DURATIONS.focus,
13+
completedPomodoros: 0,
14+
isRunning: false,
15+
tasks: [],
16+
};
17+
}
18+
19+
export function getDurationForMode(mode) {
20+
return DURATIONS[mode] || DURATIONS.focus;
21+
}
22+
23+
export function switchMode(state, mode) {
24+
const nextDuration = getDurationForMode(mode);
25+
return {
26+
...state,
27+
mode,
28+
remainingSeconds: nextDuration,
29+
isRunning: false,
30+
};
31+
}
32+
33+
export function tickTimer(state) {
34+
if (state.remainingSeconds > 1) {
35+
return {
36+
nextState: { ...state, remainingSeconds: state.remainingSeconds - 1 },
37+
completedCycle: false,
38+
};
39+
}
40+
41+
let completedPomodoros = state.completedPomodoros;
42+
let nextMode;
43+
44+
if (state.mode === 'focus') {
45+
completedPomodoros += 1;
46+
nextMode = completedPomodoros % 4 === 0 ? 'long-break' : 'short-break';
47+
} else {
48+
nextMode = 'focus';
49+
}
50+
51+
const nextDuration = getDurationForMode(nextMode);
52+
53+
return {
54+
nextState: {
55+
...state,
56+
mode: nextMode,
57+
remainingSeconds: nextDuration,
58+
completedPomodoros,
59+
isRunning: false,
60+
},
61+
completedCycle: true,
62+
};
63+
}
64+
65+
export function formatTime(totalSeconds) {
66+
const minutes = Math.floor(totalSeconds / 60)
67+
.toString()
68+
.padStart(2, '0');
69+
const seconds = (totalSeconds % 60).toString().padStart(2, '0');
70+
return `${minutes}:${seconds}`;
71+
}
72+
73+
// ===== UI helpers =====
74+
export function updateModeButtons(modeButtons, activeMode) {
75+
for (const button of modeButtons) {
76+
if (button.dataset.mode === activeMode) {
77+
button.classList.add('active');
78+
} else {
79+
button.classList.remove('active');
80+
}
81+
}
82+
}
983

1084
/**
1185
* Initializes the Pomodoro application by setting up the UI, state, and event listeners.
@@ -35,13 +109,7 @@ export function initializePomodoroApp(
35109
const iterationCount = doc.getElementById('iteration-count');
36110

37111
// Ensure all critical UI components are present before proceeding.
38-
if (
39-
!timerDisplay ||
40-
!startButton ||
41-
!pauseButton ||
42-
!resetButton ||
43-
!iterationCount
44-
) {
112+
if (!timerDisplay || !startButton || !pauseButton || !resetButton) {
45113
console.error('A critical UI element is missing from the DOM.');
46114
return;
47115
}
@@ -72,13 +140,24 @@ export function initializePomodoroApp(
72140
*/
73141
function render() {
74142
timerDisplay.textContent = formatTime(state.remainingSeconds);
75-
iterationCount.textContent = `${state.completedPomodoros}`;
143+
if (iterationCount) {
144+
iterationCount.textContent = `${state.completedPomodoros}`;
145+
}
76146
// Update the visual state of mode selection buttons.
77147
updateModeButtons(modeButtons, state.mode);
148+
updateControls();
78149
}
79150
// ========= END Live Coding =========
80151

81152
// ========= START Live Coding: Timer Controls (Start/Pause/Reset) =========
153+
/**
154+
* Keeps control buttons in sync with running/paused state for quick visual feedback.
155+
*/
156+
function updateControls() {
157+
startButton.disabled = state.isRunning;
158+
pauseButton.disabled = !state.isRunning;
159+
}
160+
82161
/**
83162
* Starts the timer loop.
84163
* If the timer is not already running, it sets up a `setInterval` to tick
@@ -90,6 +169,7 @@ export function initializePomodoroApp(
90169
return; // Prevent multiple intervals from running simultaneously.
91170
}
92171
state = { ...state, isRunning: true };
172+
render(); // Immediate UI feedback.
93173
intervalId = setInterval(() => {
94174
const { nextState, completedCycle } = tickTimer(state);
95175
state = nextState;
@@ -98,6 +178,7 @@ export function initializePomodoroApp(
98178
// Stop the timer if the session (e.g., focus, break) has ended.
99179
if (completedCycle) {
100180
stopTimer();
181+
render(); // Refresh controls after stopping.
101182
}
102183
}, 1000);
103184
}
@@ -157,5 +238,10 @@ if (globalThis.window !== undefined) {
157238
globalThis.PomodoroApp = {
158239
initializePomodoroApp,
159240
};
160-
globalThis.addEventListener('DOMContentLoaded', () => initializePomodoroApp());
241+
if (document.readyState === 'loading') {
242+
globalThis.addEventListener('DOMContentLoaded', () => initializePomodoroApp());
243+
} else {
244+
// If the script loads after DOMContentLoaded, initialize immediately.
245+
initializePomodoroApp();
246+
}
161247
}

src/pomodoro.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ <h1>GDG Pomodoro</h1>
2424
<button class="mode-btn" data-mode="short-break" aria-label="Switch to short break">Short Break</button>
2525
<button class="mode-btn" data-mode="long-break" aria-label="Switch to long break">Long Break</button>
2626
</div>
27+
<p class="iteration-meta">Completed sessions: <span id="iteration-count">0</span></p>
2728
</header>
2829

2930
<div class="timer-display" id="timer-display" aria-live="polite">25:00</div>

src/styles/pomodoro.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,15 @@ a {
220220
border-color: var(--google-yellow);
221221
}
222222

223+
.btn-primary:disabled,
224+
.btn-secondary:disabled,
225+
.btn-ghost:disabled {
226+
opacity: 0.55;
227+
cursor: not-allowed;
228+
box-shadow: none;
229+
border-color: var(--border-color);
230+
}
231+
223232
/* Lesson Guide */
224233
.lesson-notes .lesson-list {
225234
color: var(--text-main);

0 commit comments

Comments
 (0)