diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json
index b81c34e..42ee437 100644
--- a/src/_locales/de/messages.json
+++ b/src/_locales/de/messages.json
@@ -128,6 +128,53 @@
"notificationsSettings": {
"message": "Benachrichtigungen"
},
+ "pomodoroMode": {
+ "message": "Pomodoro"
+ },
+ "pomodoroSettings": {
+ "message": "Pomodoro Einstellungen"
+ },
+ "pomodoro": {
+ "message": "Pomodoro",
+ "description": "Die Pomodoro-Methode ist eine Zeitmanagementtechnik, die auf dem Wechsel von konzentrierten Arbeits- und Ruhephasen basiert. Bei der klassischen Pomodoro-Methode dauert die Arbeitsphase 25 Minuten und die Ruhephase 5 Minuten."
+ },
+ "pomodoroExplanationIcon": {
+ "message": "Wenn der Pomodoro-Modus aktiviert ist und der Betriebsmodus aktiv ist, sehen Sie dieses Symbol",
+ "description": "Während der Ruhezeiten sehen Sie dieses Erweiterungssymbol"
+ },
+ "pomodoroExplanationTime": {
+ "message": "Während des Pomodoro-Modus berücksichtigt die Erweiterung weiterhin die Zeit, die Sie auf den Seiten verbracht haben, alle Grenzen und Benachrichtigungen funktionieren."
+ },
+ "pomodoroExplanationStop": {
+ "message": "Nach dem Drücken von 'Stop' werden die Betriebs- und Ruhezeiten auf Null zurückgesetzt."
+ },
+ "pomodoroWork": {
+ "message": "Zeitraum der Arbeit"
+ },
+ "pomodoroRest": {
+ "message": "Ruhezeit"
+ },
+ "pomodoroFrequency": {
+ "message": "Anzahl der Wiederholungen"
+ },
+ "start": {
+ "message": "Starten Sie"
+ },
+ "stop": {
+ "message": "Stop"
+ },
+ "pomodoroIsEnabled": {
+ "message": "Der Pomodoro-Modus ist aktiviert"
+ },
+ "pomodoroSoundAfter": {
+ "message": "Ton nach vollständiger Periode"
+ },
+ "clickToPreview": {
+ "message": "Zum Anhören klicken"
+ },
+ "sound": {
+ "message": "Ton"
+ },
"aboutSettings": {
"message": "Über die Erweiterung"
},
@@ -316,6 +363,10 @@
"message": "Möchten Sie Kurzfilme, Kommentare, empfohlene Videos, Abonnements und andere YouTube-Ablenkungen blockieren? Wir haben eine weitere Erweiterung erstellt, mit der Sie YouTube ohne Ablenkung ansehen können.",
"description": "Install"
},
+ "completelyBlocked": {
+ "message": "Vollständig blockiert",
+ "description": "Vollständig blockieren"
+ },
"tryMyOtherApps": {
"message": "Probieren Sie meine anderen Apps aus"
},
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index 653f226..101629d 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -3,7 +3,7 @@
"message": "Web Activity Time Tracker - Block Sites"
},
"extDescription": {
- "message": "Time tracking of your web activity, limit and block distracting websites. Monitor your web usage."
+ "message": "Track time of your web activity, limit and block distracting websites. Monitor your web usage."
},
"settings": {
"message": "Settings"
@@ -128,6 +128,53 @@
"notificationsSettings": {
"message": "Notifications"
},
+ "pomodoroMode": {
+ "message": "Pomodoro"
+ },
+ "pomodoroSettings": {
+ "message": "Pomodoro Settings"
+ },
+ "pomodoro": {
+ "message": "Pomodoro",
+ "description": "The Pomodoro method is a time management technique based on alternating periods of focused work and rest. According to the classics of the Pomodoro method, the work period lasts 25 minutes, the rest period is 5 minutes."
+ },
+ "pomodoroExplanationIcon": {
+ "message": "When Pomodoro mode is on and operation mode is active, you will see this icon",
+ "description": "During rest periods, you will see this extension icon"
+ },
+ "pomodoroExplanationTime": {
+ "message": "During Pomodoro mode, the extension continues to take into account the time you spent on the sites, all limits and notifications work."
+ },
+ "pomodoroExplanationStop": {
+ "message": "After clicking 'Stop', work and rest time is reset to zero."
+ },
+ "pomodoroWork": {
+ "message": "Period of work"
+ },
+ "pomodoroRest": {
+ "message": "Period of rest"
+ },
+ "pomodoroFrequency": {
+ "message": "Number of repetitions"
+ },
+ "start": {
+ "message": "Run"
+ },
+ "stop": {
+ "message": "Stop"
+ },
+ "pomodoroIsEnabled": {
+ "message": "Pomodoro Mode is enabled"
+ },
+ "pomodoroSoundAfter": {
+ "message": "Sound after complete period"
+ },
+ "clickToPreview": {
+ "message": "Click to listen"
+ },
+ "sound": {
+ "message": "Sound"
+ },
"aboutSettings": {
"message": "About"
},
@@ -316,6 +363,10 @@
"message": "Only time intervals greater than",
"description": "are shown"
},
+ "completelyBlocked": {
+ "message": "Completely Blocked",
+ "description": "Completely Block"
+ },
"tryMyOtherApps": {
"message": "Try my other apps"
},
diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json
index 13e88aa..ce17a72 100644
--- a/src/_locales/ru/messages.json
+++ b/src/_locales/ru/messages.json
@@ -128,6 +128,53 @@
"notificationsSettings": {
"message": "Уведомления"
},
+ "pomodoroMode": {
+ "message": "Pomodoro"
+ },
+ "pomodoroSettings": {
+ "message": "Настройки Pomodoro"
+ },
+ "pomodoro": {
+ "message": "Pomodoro",
+ "description": "Метод Pomodoro — это техника тайм-менеджмента, завязанная на чередовании периодов сфокусированной работы и отдыха. По классике метода помодоро период работы длится 25 минут, период отдыха 5 минут."
+ },
+ "pomodoroExplanationIcon": {
+ "message": "Когда режим Pomodoro включен и активен режим работы, то вы будете видеть эту иконку",
+ "description": "В периоды отдыха вы будете видеть эту иконку расширения"
+ },
+ "pomodoroExplanationTime": {
+ "message": "Во время режима Pomodoro, расширение продолжает учитывать время, которые вы провели на сайтах,работают все лимиты и уведомления."
+ },
+ "pomodoroExplanationStop": {
+ "message": "После нажатия 'Остановить', время работы и отдыха обнуляется."
+ },
+ "pomodoroWork": {
+ "message": "Период работы"
+ },
+ "pomodoroRest": {
+ "message": "Период отдыха"
+ },
+ "pomodoroFrequency": {
+ "message": "Количество повторов"
+ },
+ "start": {
+ "message": "Запустить"
+ },
+ "stop": {
+ "message": "Остановить"
+ },
+ "pomodoroIsEnabled": {
+ "message": "Режим Pomodoro включен"
+ },
+ "pomodoroSoundAfter": {
+ "message": "Воспроизведение после завершения периода"
+ },
+ "clickToPreview": {
+ "message": "Нажмите для прослушивания"
+ },
+ "sound": {
+ "message": "Мелодия"
+ },
"aboutSettings": {
"message": "\u041E программе"
},
@@ -310,13 +357,17 @@
"message": "По часам"
},
"intervals": {
- "message": "Интервальны",
+ "message": "Интервалы",
"description": "Отображаются только временные интервалы, превышающие 5 секунд"
},
"intervalsChart": {
"message": "Отображаются только временные интервалы, превышающие",
"description": ""
},
+ "completelyBlocked": {
+ "message": "Полностью заблокирован",
+ "description": "Полностью заблокировать"
+ },
"tryMyOtherApps": {
"message": "Попробуйте другие мои приложения"
},
diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json
index c66b8eb..ff249eb 100644
--- a/src/_locales/zh_CN/messages.json
+++ b/src/_locales/zh_CN/messages.json
@@ -128,6 +128,53 @@
"notificationsSettings": {
"message": "通知"
},
+ "pomodoroMode": {
+ "message": "Pomodoro"
+ },
+ "pomodoroSettings": {
+ "message": "Pomodoro 设置"
+ },
+ "pomodoro": {
+ "message": "Pomodoro",
+ "description": "Pomodoro 工作法是一种时间管理技术,以交替集中工作和休息为基础。根据经典的 Pomodoro 方法,工作时间为 25 分钟,休息时间为 5 分钟。"
+ },
+ "pomodoroExplanationIcon": {
+ "message": "启用 Pomodoro 模式并激活操作模式后,您将看到以下图标",
+ "description": "在休息时间,您会看到这个扩展图标"
+ },
+ "pomodoroExplanationTime": {
+ "message": "在 Pomodoro 模式下,扩展会继续考虑你在网站上花费的时间、所有限制和通知。"
+ },
+ "pomodoroExplanationStop": {
+ "message": "按下 '停止' 键后,运行时间和休息时间将重置为零"
+ },
+ "pomodoroWork": {
+ "message": "工作期限"
+ },
+ "pomodoroRest": {
+ "message": "休息时间"
+ },
+ "pomodoroFrequency": {
+ "message": "重复次数"
+ },
+ "start": {
+ "message": "启动"
+ },
+ "stop": {
+ "message": "停止"
+ },
+ "pomodoroIsEnabled": {
+ "message": "番茄钟模式已启用"
+ },
+ "pomodoroSoundAfter": {
+ "message": "完整时段后的声音"
+ },
+ "clickToPreview": {
+ "message": "点击收听"
+ },
+ "sound": {
+ "message": "声音"
+ },
"aboutSettings": {
"message": "关于"
},
@@ -316,6 +363,10 @@
"message": "您想屏蔽短片、评论、推荐视频、订阅和其他 YouTube 干扰内容吗? 我们创建了另一个扩展程序,可帮助您不受干扰地观看 YouTube",
"description": "安装"
},
+ "completelyBlocked": {
+ "message": "完全封锁",
+ "description": "完全封锁"
+ },
"tryMyOtherApps": {
"message": "试试我的其他应用程序"
},
diff --git a/src/assets/css/dark.css b/src/assets/css/dark.css
index e4ce5bd..6d7f4e2 100644
--- a/src/assets/css/dark.css
+++ b/src/assets/css/dark.css
@@ -8,7 +8,7 @@
.dark .headerBlock .icons-block a:hover{
filter: invert(40%) sepia(94%) saturate(3371%) hue-rotate(227deg) brightness(99%) contrast(92%);
}
-.dark .headerBlock .icons-block a img {
+.dark .headerBlock .icons-block a.filter img {
filter: invert(100%) sepia(17%) saturate(0%) hue-rotate(24deg) brightness(103%) contrast(102%);
}
.dark .header-block {
@@ -63,4 +63,10 @@
}
.dark .container p.value{
color: black;
+}
+.dark .review-block p{
+ color:#303030;
+}
+.dark .pomodoro-popup-block p{
+ color:#303030;
}
\ No newline at end of file
diff --git a/src/assets/css/dashboard.css b/src/assets/css/dashboard.css
index 446b624..e3b11c9 100644
--- a/src/assets/css/dashboard.css
+++ b/src/assets/css/dashboard.css
@@ -21,7 +21,7 @@ body {
}
.description {
- font-size: 13px;
+ font-size: 14px;
color: grey;
margin: 7px 0 15px 0;
font-weight: normal;
@@ -49,7 +49,7 @@ body {
margin-left: 10px;
}
-input[type='text'] {
+input[type='number'],input[type='text'] {
height: 36px;
padding: 0 0 0 5px;
width: 400px;
diff --git a/src/assets/css/general.css b/src/assets/css/general.css
index 7f4a4ad..bac2baf 100644
--- a/src/assets/css/general.css
+++ b/src/assets/css/general.css
@@ -29,6 +29,9 @@
.mb-20 {
margin-bottom: 20px;
}
+.ml-5 {
+ margin-left: 5px;
+}
.ml-10 {
margin-left: 10px;
}
@@ -57,6 +60,10 @@
text-align: center;
}
+.mt-10 {
+ margin-top: 10px;
+}
+
.mt-15 {
margin-top: 15px;
}
diff --git a/src/assets/icons/pomodoro-rest.png b/src/assets/icons/pomodoro-rest.png
new file mode 100644
index 0000000..5c8a25e
Binary files /dev/null and b/src/assets/icons/pomodoro-rest.png differ
diff --git a/src/assets/icons/pomodoro.png b/src/assets/icons/pomodoro.png
new file mode 100644
index 0000000..1bca502
Binary files /dev/null and b/src/assets/icons/pomodoro.png differ
diff --git a/src/assets/icons/pomodoro.svg b/src/assets/icons/pomodoro.svg
new file mode 100644
index 0000000..d3ec461
--- /dev/null
+++ b/src/assets/icons/pomodoro.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/start.svg b/src/assets/icons/start.svg
new file mode 100644
index 0000000..144f821
--- /dev/null
+++ b/src/assets/icons/start.svg
@@ -0,0 +1,17 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/stop.svg b/src/assets/icons/stop.svg
new file mode 100644
index 0000000..8d35481
--- /dev/null
+++ b/src/assets/icons/stop.svg
@@ -0,0 +1,17 @@
+
+
+
\ No newline at end of file
diff --git a/src/assets/pomodoro-sounds/1.mp3 b/src/assets/pomodoro-sounds/1.mp3
new file mode 100644
index 0000000..2c4babb
Binary files /dev/null and b/src/assets/pomodoro-sounds/1.mp3 differ
diff --git a/src/assets/pomodoro-sounds/10.mp3 b/src/assets/pomodoro-sounds/10.mp3
new file mode 100644
index 0000000..371a5b8
Binary files /dev/null and b/src/assets/pomodoro-sounds/10.mp3 differ
diff --git a/src/assets/pomodoro-sounds/11.mp3 b/src/assets/pomodoro-sounds/11.mp3
new file mode 100644
index 0000000..e000b73
Binary files /dev/null and b/src/assets/pomodoro-sounds/11.mp3 differ
diff --git a/src/assets/pomodoro-sounds/12.mp3 b/src/assets/pomodoro-sounds/12.mp3
new file mode 100644
index 0000000..b949f38
Binary files /dev/null and b/src/assets/pomodoro-sounds/12.mp3 differ
diff --git a/src/assets/pomodoro-sounds/13.mp3 b/src/assets/pomodoro-sounds/13.mp3
new file mode 100644
index 0000000..a255ca5
Binary files /dev/null and b/src/assets/pomodoro-sounds/13.mp3 differ
diff --git a/src/assets/pomodoro-sounds/2.mp3 b/src/assets/pomodoro-sounds/2.mp3
new file mode 100644
index 0000000..2e526e3
Binary files /dev/null and b/src/assets/pomodoro-sounds/2.mp3 differ
diff --git a/src/assets/pomodoro-sounds/3.mp3 b/src/assets/pomodoro-sounds/3.mp3
new file mode 100644
index 0000000..185ddb4
Binary files /dev/null and b/src/assets/pomodoro-sounds/3.mp3 differ
diff --git a/src/assets/pomodoro-sounds/4.mp3 b/src/assets/pomodoro-sounds/4.mp3
new file mode 100644
index 0000000..4fcab3f
Binary files /dev/null and b/src/assets/pomodoro-sounds/4.mp3 differ
diff --git a/src/assets/pomodoro-sounds/5.mp3 b/src/assets/pomodoro-sounds/5.mp3
new file mode 100644
index 0000000..8009774
Binary files /dev/null and b/src/assets/pomodoro-sounds/5.mp3 differ
diff --git a/src/assets/pomodoro-sounds/6.mp3 b/src/assets/pomodoro-sounds/6.mp3
new file mode 100644
index 0000000..21d0827
Binary files /dev/null and b/src/assets/pomodoro-sounds/6.mp3 differ
diff --git a/src/assets/pomodoro-sounds/7.mp3 b/src/assets/pomodoro-sounds/7.mp3
new file mode 100644
index 0000000..901fc3d
Binary files /dev/null and b/src/assets/pomodoro-sounds/7.mp3 differ
diff --git a/src/assets/pomodoro-sounds/8.mp3 b/src/assets/pomodoro-sounds/8.mp3
new file mode 100644
index 0000000..8843950
Binary files /dev/null and b/src/assets/pomodoro-sounds/8.mp3 differ
diff --git a/src/assets/pomodoro-sounds/9.mp3 b/src/assets/pomodoro-sounds/9.mp3
new file mode 100644
index 0000000..59845ea
Binary files /dev/null and b/src/assets/pomodoro-sounds/9.mp3 differ
diff --git a/src/background.ts b/src/background.ts
index 3a12b2c..6feb8a6 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -6,17 +6,26 @@ import { Settings } from './functions/settings';
import { StorageParams } from './storage/storage-params';
import { injecStorage } from './storage/inject-storage';
import { todayLocalDate } from './utils/date';
+import { checkPomodoro } from './functions/pomodoro';
+import { Messages } from './utils/messages';
+import { injectTabsRepositorySingleton } from './repository/inject-tabs-repository';
logger.log('Start background script');
+let pomodoroTimer: number;
self.onerror = err => {
console.error('Unhandled error:', err);
};
-Browser.storage.onChanged.addListener((changes, namespace) => {
+Browser.storage.onChanged.addListener(async (changes, namespace) => {
for (var key in changes) {
if (Object.values(StorageParams).includes(key as StorageParams))
- Settings.getInstance().reloadSetting(key as StorageParams);
+ await Settings.getInstance().reloadSetting(key as StorageParams);
+
+ if (key == StorageParams.IS_POMODORO_ENABLED) {
+ const value = changes[StorageParams.IS_POMODORO_ENABLED].newValue;
+ pomodoro(value);
+ }
}
});
@@ -54,5 +63,30 @@ Browser.windows.onFocusChanged.addListener(() => {
logger.log('onFocusChanged');
});
+async function pomodoro(value?: boolean) {
+ if (value == undefined) {
+ const settingsStorage = injecStorage();
+ value = await settingsStorage.getValue(StorageParams.IS_POMODORO_ENABLED);
+ }
+ if (value == true) pomodoroTimer = setInterval(checkPomodoro, 1000);
+ else clearInterval(pomodoroTimer);
+}
+
+pomodoro();
scheduleJobs();
initTracker();
+
+Browser.runtime.onMessage.addListener(async message => {
+ if (message == Messages.ClearAllData) {
+ const storage = injecStorage();
+ const repo = await injectTabsRepositorySingleton();
+ repo.removeAllTabs();
+ await storage.saveTabs([]);
+ }
+ if (message.message == Messages.Restore) {
+ const storage = injecStorage();
+ await storage.saveTabs(message.data);
+ const repo = await injectTabsRepositorySingleton();
+ repo.initAsync();
+ }
+});
diff --git a/src/block.html b/src/block.html
index f0075ee..1b72d23 100644
--- a/src/block.html
+++ b/src/block.html
@@ -1,4 +1,4 @@
-
+
@@ -11,6 +11,5 @@
-
diff --git a/src/components/Dashboad.vue b/src/components/Dashboad.vue
index b94beb1..9658ff2 100644
--- a/src/components/Dashboad.vue
+++ b/src/components/Dashboad.vue
@@ -71,7 +71,19 @@ function openChart(type: TypeOfChart) {
height: 390px;
}
.chart-btn {
- background-color: rgb(202, 202, 202);
+ background-color: rgb(192, 192, 192);
+ color: #fff;
+ border-radius: 3px;
+ height: 36px;
+ line-height: 35px;
+ padding: 0 20px;
+ border: 0;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ min-width: 80px;
+ text-align: center;
+ width: 200px;
}
.chart-btn.active {
diff --git a/src/components/Pomodoro.vue b/src/components/Pomodoro.vue
new file mode 100644
index 0000000..cf58928
--- /dev/null
+++ b/src/components/Pomodoro.vue
@@ -0,0 +1,254 @@
+
+ {{ t('pomodoro.message') }}
+
+ {{ t('pomodoro.description') }}
+
+
+
+ {{ t('pomodoroExplanationIcon.message') }}
+
.
+ {{ t('pomodoroExplanationIcon.description') }}
+
.
+
+
+ {{ t('pomodoroExplanationTime.message') }}
+
+
{{ t('pomodoroExplanationStop.message') }}
+
+
+
+
{{ t('pomodoroWork.message') }}
+
+
+
+
+
{{ t('pomodoroRest.message') }}
+
+
+
+
+
{{ t('pomodoroFrequency.message') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/PomodoroInfo.vue b/src/components/PomodoroInfo.vue
new file mode 100644
index 0000000..23f776e
--- /dev/null
+++ b/src/components/PomodoroInfo.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/PomodoroSoundsSelector.vue b/src/components/PomodoroSoundsSelector.vue
new file mode 100644
index 0000000..e9b8c8d
--- /dev/null
+++ b/src/components/PomodoroSoundsSelector.vue
@@ -0,0 +1,69 @@
+
+ {{ t('pomodoroSoundAfter.message') }}
+
+
+ {{ t('clickToPreview.message') }}
+
+
+
+
+
+
+
diff --git a/src/components/Review.vue b/src/components/Review.vue
index b12d619..c1cf061 100644
--- a/src/components/Review.vue
+++ b/src/components/Review.vue
@@ -82,6 +82,7 @@ async function openStore() {
display: inline-block;
margin: 8px;
font-size: 17px;
+ font-weight: 500;
}
.review-block img {
padding: 9px 0 0 0;
diff --git a/src/functions/playSound.ts b/src/functions/playSound.ts
new file mode 100644
index 0000000..61eb7f2
--- /dev/null
+++ b/src/functions/playSound.ts
@@ -0,0 +1,7 @@
+import Browser from 'webextension-polyfill';
+import { PomodoroSounds } from '../utils/pomodoro';
+
+export function playSound(sound: PomodoroSounds) {
+ const myAudio = new Audio(Browser.runtime.getURL(`assets/pomodoro-sounds/${sound}`));
+ myAudio.play();
+}
diff --git a/src/functions/pomodoro.ts b/src/functions/pomodoro.ts
index 49c74bf..afb639f 100644
--- a/src/functions/pomodoro.ts
+++ b/src/functions/pomodoro.ts
@@ -1,64 +1,152 @@
import { addSeconds } from 'date-fns';
import { injecStorage } from '../storage/inject-storage';
import { StorageParams } from '../storage/storage-params';
-import { Time, timeToSeconds } from '../utils/time';
import { useBadge, BadgeIcon, BadgeColor } from './useBadge';
+import { Settings } from './settings';
+import Browser from 'webextension-polyfill';
+import { logger } from '../utils/logger';
+import { Messages } from '../utils/messages';
+import { isDateEqual } from '../utils/date';
+import { createOffscreen } from '../offscreen/index';
export async function checkPomodoro() {
- function isTargetPeriod(isRest: boolean) {
+ type PomodoroPeriod = {
+ period: Period;
+ isTargetPeriod: boolean;
+ isTargetPeriodFinishedNow: boolean;
+ };
+
+ enum Period {
+ work = 'WORK',
+ rest = 'REST',
+ finished = 'FINISH',
+ }
+
+ function isTargetPeriod(period: Period): PomodoroPeriod {
+ let isPomodoroTargetPeriodEnd;
for (let index = 1; index <= frequency; index++) {
- const plusWorkingTime = timeToSeconds(workTime) * (isRest ? index : index--);
- const plusRestTime = timeToSeconds(restTime) * index--;
+ let ind = period == Period.work ? index - 1 : index;
+ const plusWorkingTime = workTime * ind;
+ const plusRestTime = (restTime + 1) * (index - 1);
const isPomodoroTargetPeriodStart = addSeconds(startTime, plusWorkingTime + plusRestTime);
- const isPomodoroTargetPeriodEnd = addSeconds(
- startTime,
- plusWorkingTime + plusRestTime + timeToSeconds(workTime),
- );
+ isPomodoroTargetPeriodEnd = addSeconds(startTime, plusWorkingTime + plusRestTime + workTime);
const isTargetPeriod =
- now.getTime() >= isPomodoroTargetPeriodStart.getTime() &&
- now.getTime() <= isPomodoroTargetPeriodEnd.getTime();
+ now >= isPomodoroTargetPeriodStart &&
+ (now <= isPomodoroTargetPeriodEnd || addSeconds(now, -1) <= isPomodoroTargetPeriodEnd);
- if (isTargetPeriod) return true;
+ if (isTargetPeriod) {
+ console.log(
+ now,
+ isPomodoroTargetPeriodEnd,
+ isDateEqual(now, isPomodoroTargetPeriodEnd) ||
+ isDateEqual(addSeconds(now, -1), isPomodoroTargetPeriodEnd),
+ period,
+ );
+ return {
+ period: period,
+ isTargetPeriod: isTargetPeriod,
+ isTargetPeriodFinishedNow:
+ isDateEqual(now, isPomodoroTargetPeriodEnd) ||
+ isDateEqual(addSeconds(now, -1), isPomodoroTargetPeriodEnd),
+ };
+ }
}
- return false;
+ return {
+ period: Period.finished,
+ isTargetPeriod: false,
+ isTargetPeriodFinishedNow: false,
+ };
+ }
+
+ async function play(period: Period) {
+ function getSound() {
+ switch (period) {
+ case Period.work:
+ return StorageParams.POMODORO_AUDIO_AFTER_WORK;
+ case Period.rest:
+ return StorageParams.POMODORO_AUDIO_AFTER_REST;
+ case Period.finished:
+ return StorageParams.POMODORO_AUDIO_AFTER_FINISHED;
+ }
+ }
+ logger.log(`[Pomodoro] ${period}`);
+ const sound = await storage.getValue(getSound());
+ await createOffscreen();
+ await Browser.runtime.sendMessage({
+ message: Messages.PlayAudio,
+ sound: sound,
+ offscreen: true,
+ });
}
const storage = injecStorage();
- const isPomodoroEnabled = (await storage.getValue(StorageParams.IS_POMODORO_ENABLED)) as boolean;
+ const isPomodoroEnabled = (await Settings.getInstance().getSetting(
+ StorageParams.IS_POMODORO_ENABLED,
+ )) as boolean;
if (!isPomodoroEnabled) return;
- const startTime = (await storage.getValue(StorageParams.POMODORO_START_TIME)) as Date;
- const workTime = (await storage.getValue(StorageParams.POMODORO_INTERVAL_WORK)) as Time;
- const restTime = (await storage.getValue(StorageParams.POMODORO_INTERVAL_REST)) as Time;
- const frequency = (await storage.getValue(StorageParams.POMODORO_FREQUENCY)) as number;
+ const startTime = new Date(
+ (await Settings.getInstance().getSetting(StorageParams.POMODORO_START_TIME)) as string,
+ );
+ const workTime = (await Settings.getInstance().getSetting(
+ StorageParams.POMODORO_INTERVAL_WORK,
+ )) as number;
+ const restTime = (await Settings.getInstance().getSetting(
+ StorageParams.POMODORO_INTERVAL_REST,
+ )) as number;
+ const frequency = (await Settings.getInstance().getSetting(
+ StorageParams.POMODORO_FREQUENCY,
+ )) as number;
const now = new Date();
- const pomodoroEndTime = addSeconds(
- startTime,
- timeToSeconds(workTime) * frequency + timeToSeconds(restTime) * frequency,
- );
+ const pomodoroEndTime = addSeconds(startTime, workTime * frequency + restTime * frequency);
+
+ const activeTab = await Browser.tabs.query({ active: true });
+
+ if (now >= pomodoroEndTime) {
+ if (isDateEqual(now, pomodoroEndTime)) {
+ logger.log(`[Pomodoro] Pomodoro finished`);
+ await play(Period.finished);
+ }
- if (pomodoroEndTime > now) {
await storage.saveValue(StorageParams.IS_POMODORO_ENABLED, false);
await storage.saveValue(StorageParams.POMODORO_START_TIME, null);
+ await useBadge({
+ tabId: activeTab[0].id,
+ text: null,
+ color: BadgeColor.none,
+ icon: BadgeIcon.default,
+ });
return;
}
- const isWork = isTargetPeriod(false);
- const isRest = isTargetPeriod(true);
+ let target = isTargetPeriod(Period.work);
+ const isWork = target.isTargetPeriod;
- if (isWork)
+ if (isWork) {
await useBadge({
- text: '',
+ tabId: activeTab[0].id,
+ text: null,
color: BadgeColor.none,
icon: BadgeIcon.pomodoroWorkingTime,
});
- if (isRest)
- await useBadge({
- text: '',
- color: BadgeColor.none,
- icon: BadgeIcon.pomodoroRestTime,
- });
+ } else {
+ target = isTargetPeriod(Period.rest);
+ if (target.isTargetPeriod) {
+ await useBadge({
+ tabId: activeTab[0].id,
+ text: null,
+ color: BadgeColor.none,
+ icon: BadgeIcon.pomodoroRestTime,
+ });
+ }
+ }
+
+ if (target.isTargetPeriodFinishedNow) await play(target.period);
+
+ return {
+ isWork,
+ };
}
diff --git a/src/functions/useBadge.ts b/src/functions/useBadge.ts
index ddd9a6d..8854b25 100644
--- a/src/functions/useBadge.ts
+++ b/src/functions/useBadge.ts
@@ -1,16 +1,16 @@
import Browser from 'webextension-polyfill';
export interface BadgeState {
- text: string;
+ text: string | null;
color: BadgeColor;
tabId?: number;
icon?: BadgeIcon;
}
export enum BadgeIcon {
- timer = '/assets/icons/128x128.png',
- pomodoroWorkingTime = '/assets/icons/empty.png',
- pomodoroRestTime = '/assets/icons/empty.png',
+ default = '/128x128.png',
+ pomodoroWorkingTime = '/assets/icons/pomodoro.png',
+ pomodoroRestTime = '/assets/icons/pomodoro-rest.png',
}
export enum BadgeColor {
@@ -21,13 +21,22 @@ export enum BadgeColor {
}
export async function useBadge(badge: BadgeState): Promise {
- await Browser.action.setBadgeBackgroundColor({ color: badge.color });
+ if (badge.color != BadgeColor.none)
+ await Browser.action.setBadgeBackgroundColor({ color: badge.color });
await Browser.action.setBadgeText({
tabId: badge.tabId,
text: badge.text,
});
- if (badge.icon)
+ if (badge.icon) {
await Browser.action.setIcon({
path: badge.icon,
});
+ await Browser.action.setBadgeText({
+ tabId: badge.tabId,
+ text: badge.text,
+ });
+ } else
+ await Browser.action.setIcon({
+ path: BadgeIcon.default,
+ });
}
diff --git a/src/manifest.json b/src/manifest.json
index 6523135..736dc37 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -19,7 +19,8 @@
"idle",
"unlimitedStorage",
"alarms",
- "notifications"
+ "notifications",
+ "offscreen"
],
"offline_enabled": true,
"background": {
@@ -28,5 +29,9 @@
"action": {
"default_popup": "src/popup.html",
"default_title": "Web Activity Time Tracker"
- }
+ },
+ "web_accessible_resources": [{
+ "resources": ["assets/pomodoro-sounds/*.mp3"],
+ "matches": [""]
+ }]
}
diff --git a/src/offscreen.html b/src/offscreen.html
new file mode 100644
index 0000000..9c5de41
--- /dev/null
+++ b/src/offscreen.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Offscreen
+
+
+
+
+
+
+
diff --git a/src/offscreen.ts b/src/offscreen.ts
new file mode 100644
index 0000000..b4c0574
--- /dev/null
+++ b/src/offscreen.ts
@@ -0,0 +1,23 @@
+import Browser from 'webextension-polyfill';
+import { Messages } from './utils/messages';
+
+console.log('ofscreen');
+
+Browser.runtime.onMessage.addListener(msg => {
+ console.log('ofscreen message');
+ if (msg.message == Messages.PlayAudio) {
+ if (msg.offscreen == undefined) return;
+
+ playAudio(msg.sound);
+ }
+});
+
+function playAudio(sound: string) {
+ const audio = document.querySelector('audio');
+ if (!audio) return;
+
+ const path = Browser.runtime.getURL(`../assets/pomodoro-sounds/${sound}`);
+ audio.src = path;
+ audio.volume = 1;
+ audio.play();
+}
diff --git a/src/offscreen/index.ts b/src/offscreen/index.ts
new file mode 100644
index 0000000..6f8fd85
--- /dev/null
+++ b/src/offscreen/index.ts
@@ -0,0 +1,12 @@
+import Browser from 'webextension-polyfill';
+
+export async function createOffscreen() {
+ const path = 'src/offscreen.html';
+ const offscreenUrl = Browser.runtime.getURL(path);
+ if (await chrome.offscreen.hasDocument()) return;
+ await chrome.offscreen.createDocument({
+ url: offscreenUrl,
+ reasons: ['AUDIO_PLAYBACK'],
+ justification: 'Play audio sounds',
+ });
+}
diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue
index 27599a6..a3108e8 100644
--- a/src/pages/Dashboard.vue
+++ b/src/pages/Dashboard.vue
@@ -90,6 +90,27 @@
+
+
+
+
+
+
+
@@ -84,6 +88,7 @@
+
@@ -93,6 +98,7 @@ import { useI18n } from 'vue-i18n';
import TabList from '../components/TabList.vue';
import ByDays from '../components/ByDays.vue';
import Review from '../components/Review.vue';
+import PomodoroInfo from '../components/PomodoroInfo.vue';
import { openPage } from '../utils/open-page';
import { SettingsTab, TypeOfList } from '../utils/enums';
import { injecStorage } from '../storage/inject-storage';
@@ -137,7 +143,7 @@ function updateTab() {
}
.headerBlock .header {
font-size: 16px;
- padding: 0 10px;
+ padding: 0 0 0 5px;
display: inline-block;
font-weight: 600;
color: #4a4a4a;
@@ -153,7 +159,7 @@ function updateTab() {
}
.headerBlock .icons-block {
float: right;
- margin: 7px 10px 0 0;
+ margin: 7px 0 0 0;
}
.headerBlock .icons-block a:hover {
@@ -172,6 +178,5 @@ function updateTab() {
}
.headerBlock .icons-block .dark-mode-icon {
vertical-align: middle;
- margin-right: 10px;
}
diff --git a/src/storage/storage-params.ts b/src/storage/storage-params.ts
index 612b2ae..09c001f 100644
--- a/src/storage/storage-params.ts
+++ b/src/storage/storage-params.ts
@@ -1,6 +1,7 @@
import { Tab } from '../entity/tab';
import { TimeInterval } from '../entity/time-interval';
-import { HOUR } from '../utils/time';
+import { PomodoroSounds } from '../utils/pomodoro';
+import { HOUR, MINUTE_IN_SECONDS } from '../utils/time';
export enum StorageParams {
BLACK_LIST = 'black_list',
@@ -20,6 +21,14 @@ export enum StorageParams {
INSTALL_DATE = 'install-date',
PROMO_CLEAR_YOUTUBE_ON_LIMITS = 'promo-clear-youtube-on-limits',
PROMO_CLEAR_YOUTUBE_ON_BLOCK = 'promo-clear-youtube-on-block',
+ IS_POMODORO_ENABLED = 'is-pomodoro-enabled',
+ POMODORO_START_TIME = 'pomodoro-start-time',
+ POMODORO_INTERVAL_WORK = 'pomodoro-interval-work',
+ POMODORO_AUDIO_AFTER_WORK = 'pomodoro-audio-after-work',
+ POMODORO_AUDIO_AFTER_REST = 'pomodoro-audio-after-rest',
+ POMODORO_AUDIO_AFTER_FINISHED = 'pomodoro-audio-after-finished',
+ POMODORO_INTERVAL_REST = 'pomodoro-interval-rest',
+ POMODORO_FREQUENCY = 'pomodoro-frequency',
}
export enum StorageDeserializeParam {
@@ -63,6 +72,13 @@ export const DAILY_SUMMARY_NOTIFICATION_TIME_DEFAULT = (20 * HOUR) / 1000;
export const DAILY_NOTIFICATION_DEFAULT = true;
export const SHOW_CHANGELOG_DEFAULT = false;
export const SHOW_PROMO_CLEAR_YOUTUBE_DEFAULT = false;
+export const IS_POMODORO_ENABLED_DEFAULT = false;
+export const POMODORO_INTERVAL_WORK_DEFAULT = 25 * MINUTE_IN_SECONDS;
+export const POMODORO_INTERVAL_REST_DEFAULT = 5 * MINUTE_IN_SECONDS;
+export const POMODORO_FREQUENCY_DEFAULT = 3;
+export const POMODORO_AUDIO_AFTER_WORK_DEFAULT = PomodoroSounds['Sound 3'];
+export const POMODORO_AUDIO_AFTER_REST_DEFAULT = PomodoroSounds['Sound 8'];
+export const POMODORO_AUDIO_AFTER_FINISHED_DEFAULT = PomodoroSounds['Sound 10'];
export function getDefaultValue(param: StorageParams) {
switch (param) {
@@ -93,5 +109,19 @@ export function getDefaultValue(param: StorageParams) {
case StorageParams.PROMO_CLEAR_YOUTUBE_ON_BLOCK:
case StorageParams.PROMO_CLEAR_YOUTUBE_ON_LIMITS:
return SHOW_PROMO_CLEAR_YOUTUBE_DEFAULT;
+ case StorageParams.IS_POMODORO_ENABLED:
+ return IS_POMODORO_ENABLED_DEFAULT;
+ case StorageParams.POMODORO_INTERVAL_WORK:
+ return POMODORO_INTERVAL_WORK_DEFAULT;
+ case StorageParams.POMODORO_INTERVAL_REST:
+ return POMODORO_INTERVAL_REST_DEFAULT;
+ case StorageParams.POMODORO_FREQUENCY:
+ return POMODORO_FREQUENCY_DEFAULT;
+ case StorageParams.POMODORO_AUDIO_AFTER_WORK:
+ return POMODORO_AUDIO_AFTER_WORK_DEFAULT;
+ case StorageParams.POMODORO_AUDIO_AFTER_REST:
+ return POMODORO_AUDIO_AFTER_REST_DEFAULT;
+ case StorageParams.POMODORO_AUDIO_AFTER_FINISHED:
+ return POMODORO_AUDIO_AFTER_FINISHED_DEFAULT;
}
}
diff --git a/src/tracker.ts b/src/tracker.ts
index af2bc57..7e8a4a2 100644
--- a/src/tracker.ts
+++ b/src/tracker.ts
@@ -39,7 +39,7 @@ async function trackTime() {
if (isValidPage(activeTab)) {
const activeDomain = extractHostname(activeTab!.url);
- if (await isInBlackList(activeDomain)) {
+ if ((await isInBlackList(activeDomain)) && (await canChangeBadge())) {
await useBadge({
tabId: activeTab?.id,
text: 'n/a',
@@ -126,18 +126,20 @@ async function mainTracker(
const viewInBadge = await Settings.getInstance().getSetting(StorageParams.VIEW_TIME_IN_BADGE);
- if (viewInBadge)
- await useBadge({
- tabId: activeTab?.id,
- text: convertSummaryTimeToBadgeString(tab.days.at(-1)!.summary),
- color: BadgeColor.blue,
- });
- else
- await useBadge({
- tabId: activeTab?.id,
- text: '',
- color: BadgeColor.red,
- });
+ if (await canChangeBadge()) {
+ if (viewInBadge)
+ await useBadge({
+ tabId: activeTab?.id,
+ text: convertSummaryTimeToBadgeString(tab.days.at(-1)!.summary),
+ color: BadgeColor.blue,
+ });
+ else
+ await useBadge({
+ tabId: activeTab?.id,
+ text: null,
+ color: BadgeColor.none,
+ });
+ }
} else await closeOpenInterval();
}
@@ -161,17 +163,6 @@ async function saveTabs() {
await storage.saveTabs(tabs);
}
-Browser.runtime.onMessage.addListener(async message => {
- if (message == Messages.ClearAllData) {
- const storage = injecStorage();
- const repo = await injectTabsRepositorySingleton();
- repo.removeAllTabs();
- await storage.saveTabs([]);
- }
- if (message.message == Messages.Restore) {
- const storage = injecStorage();
- await storage.saveTabs(message.data);
- const repo = await injectTabsRepositorySingleton();
- repo.initAsync();
- }
-});
+async function canChangeBadge() {
+ return !(await Settings.getInstance().getSetting(StorageParams.IS_POMODORO_ENABLED)) as boolean;
+}
diff --git a/src/utils/converter.ts b/src/utils/converter.ts
index 06ac8f5..a80a5a7 100644
--- a/src/utils/converter.ts
+++ b/src/utils/converter.ts
@@ -2,7 +2,7 @@ import i18n, { getMessagesFromLocale } from '../plugins/i18n';
import { HOUR, HOUR_IN_SECONDS, MINUTE, MINUTE_IN_SECONDS, Time } from './time';
export function convertHHMMToSeconds(hours: number, minutes: number) {
- return hours * 3600 + minutes * 60;
+ return hours * HOUR_IN_SECONDS + minutes * MINUTE_IN_SECONDS;
}
export function convertHHMMToMilliSeconds(hours: number, minutes: number) {
@@ -10,9 +10,9 @@ export function convertHHMMToMilliSeconds(hours: number, minutes: number) {
}
export function convertSecondsToHHMM(seconds: number): Time {
- const hours = Math.floor(seconds / 3600);
- const totalSeconds = seconds % 3600;
- const mins = Math.floor(totalSeconds / 60);
+ const hours = Math.floor(seconds / HOUR_IN_SECONDS);
+ const totalSeconds = seconds % HOUR_IN_SECONDS;
+ const mins = Math.floor(totalSeconds / MINUTE_IN_SECONDS);
return {
hours: hours,
diff --git a/src/utils/date.ts b/src/utils/date.ts
index 2fa2cda..3be4450 100644
--- a/src/utils/date.ts
+++ b/src/utils/date.ts
@@ -29,3 +29,14 @@ export function ranges() {
},
];
}
+
+export function isDateEqual(first: Date, second: Date) {
+ return (
+ first.getFullYear() == second.getFullYear() &&
+ first.getMonth() == second.getMonth() &&
+ first.getDate() == second.getDate() &&
+ first.getHours() == second.getHours() &&
+ first.getMinutes() == second.getMinutes() &&
+ first.getSeconds() == second.getSeconds()
+ );
+}
diff --git a/src/utils/enums.ts b/src/utils/enums.ts
index 2e94d73..bc270f2 100644
--- a/src/utils/enums.ts
+++ b/src/utils/enums.ts
@@ -17,6 +17,7 @@ export enum SettingsTab {
WhiteList,
Limits,
Notifications,
+ Pomodoro,
About,
}
diff --git a/src/utils/extension-tabs.ts b/src/utils/extension-tabs.ts
index 7ff66e6..f89c8fe 100644
--- a/src/utils/extension-tabs.ts
+++ b/src/utils/extension-tabs.ts
@@ -16,6 +16,8 @@ export function getStringTab(tab: SettingsTab) {
return 'whitelist';
case SettingsTab.Notifications:
return 'notifications';
+ case SettingsTab.Pomodoro:
+ return 'pomodoro';
}
}
@@ -35,5 +37,7 @@ export function getEnumValueTab(tab: string) {
return SettingsTab.WhiteList;
case 'notifications':
return SettingsTab.Notifications;
+ case 'pomodoro':
+ return SettingsTab.Pomodoro;
}
}
diff --git a/src/utils/messages.ts b/src/utils/messages.ts
index 61d7e3c..4f7f239 100644
--- a/src/utils/messages.ts
+++ b/src/utils/messages.ts
@@ -2,4 +2,5 @@ export enum Messages {
RescheduleJobs = 'reschedule-jobs',
ClearAllData = 'clear-data',
Restore = 'restore-data',
+ PlayAudio = 'play-audio',
}
diff --git a/src/utils/pomodoro.ts b/src/utils/pomodoro.ts
new file mode 100644
index 0000000..c1a546d
--- /dev/null
+++ b/src/utils/pomodoro.ts
@@ -0,0 +1,22 @@
+import { StorageParams } from '../storage/storage-params';
+
+export type PomodoroAudioParams =
+ | StorageParams.POMODORO_AUDIO_AFTER_WORK
+ | StorageParams.POMODORO_AUDIO_AFTER_REST
+ | StorageParams.POMODORO_AUDIO_AFTER_FINISHED;
+
+export enum PomodoroSounds {
+ 'Sound 1' = '1.mp3',
+ 'Sound 2' = '2.mp3',
+ 'Sound 3' = '3.mp3',
+ 'Sound 4' = '4.mp3',
+ 'Sound 5' = '5.mp3',
+ 'Sound 6' = '6.mp3',
+ 'Sound 7' = '7.mp3',
+ 'Sound 8' = '8.mp3',
+ 'Sound 9' = '9.mp3',
+ 'Sound 10' = '10.mp3',
+ 'Sound 11' = '11.mp3',
+ 'Sound 12' = '12.mp3',
+ 'Sound 13' = '13.mp3',
+}
diff --git a/vite.config.ts b/vite.config.ts
index c2fb681..b8ef92a 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -22,6 +22,7 @@ function generateManifest() {
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
build: {
+ assetsInlineLimit: 1024,
rollupOptions: {
output: {
assetFileNames: assetInfo => {
@@ -54,10 +55,13 @@ export default defineConfig(({ mode }) => ({
webExtension({
manifest: generateManifest,
watchFilePaths: ['package.json', 'manifest.json'],
- additionalInputs: ['src/block.html', 'src/welcome.html'],
+ additionalInputs: ['src/block.html', 'src/welcome.html', 'src/offscreen.html'],
}),
copy({
- targets: [{ src: 'src/_locales', dest: 'dist' }],
+ targets: [
+ { src: 'src/_locales', dest: 'dist' },
+ { src: 'src/assets/pomodoro-sounds', dest: 'dist/assets' },
+ ],
}),
],
optimizeDeps: {