Skip to content
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
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 120,
"plugins": ["prettier-plugin-space-before-function-paren"]
}
415 changes: 37 additions & 378 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "twenty",
"productName": "Twenty",
"description": "Customizable timer that reminds you to take eye breaks",
"version": "0.2.1",
"version": "0.3.1",
"repository": "https://github.com/jalenng/twenty",
"url": "https://github.com/jalenng/twenty#readme",
"author": {
Expand Down Expand Up @@ -55,6 +55,8 @@
"win32-api": "^19.8.1"
},
"devDependencies": {
"prettier": "^3.0.0",
"prettier-plugin-space-before-function-paren": "^0.0.10",
"concurrently": "^7.3.0",
"cross-env": "^7.0.3",
"electron": "^20.0.0",
Expand Down
14,429 changes: 14,429 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions public/app/createWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const TYPE_OPTIONS = {
const POPUP_OPTIONS = {
size: {
width: 360,
height: 90
height: 150
},
gapFromEdge: 12
}
Expand All @@ -81,16 +81,13 @@ function createWindow (type, destination = '', display = null, isPopup = false)

// Load URL
window.loadURL(
isDev
? `http://localhost:3000#/${destination}`
: `file://${__dirname}/../../build/index.html#/${destination}` // eslint-disable-line n/no-path-concat
isDev ? `http://localhost:3000#/${destination}` : `file://${__dirname}/../../build/index.html#/${destination}` // eslint-disable-line n/no-path-concat
)

let mainWindowState

switch (type) {
case 'main':

// Update and remember the position of the main window
mainWindowState = windowStateKeeper({})

Expand Down Expand Up @@ -127,7 +124,7 @@ function createWindow (type, destination = '', display = null, isPopup = false)
// If notification is a popup window, show when ready and set always-on-top level
if (isPopup) {
window.setAlwaysOnTop(true, 'screen-saver')
window.on('ready-to-show', () => window.show())
window.on('ready-to-show', () => window.showInactive())
}

break
Expand Down Expand Up @@ -171,7 +168,9 @@ function createWindow (type, destination = '', display = null, isPopup = false)
window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })

// Prevent opening new windows
window.webContents.setWindowOpenHandler('new-window', () => { return ({ action: 'deny' }) })
window.webContents.setWindowOpenHandler('new-window', () => {
return { action: 'deny' }
})

return window
}
Expand Down
4 changes: 4 additions & 0 deletions public/app/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ window.breakSys = {
eventSystem: new EventSystem()
}

window.cancelBreak = () => ipcRenderer.invoke('cancel-break')
window.delayBreak = (minutes) => ipcRenderer.invoke('delay-break', minutes)
window.triggerBreak = () => ipcRenderer.invoke('trigger-break')

window.blockerSys = {
getBlockers: () => { ipcRenderer.send('get-blockers') },
clear: () => { ipcRenderer.invoke('clear-blockers') },
Expand Down
8 changes: 7 additions & 1 deletion public/app/setupApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { appPath, isDev } = require('../constants')
const { mainWindow } = require('./windowManager')
const createWindow = require('./createWindow')
const store = require('../store/store')
const waitForDevServer = require('./waitForDevServer')

const { createTray } = require('./tray')
const { menu } = require('./menu')
Expand All @@ -25,7 +26,12 @@ if (!isDev) {
}

/** Configure app event handlers */
app.whenReady().then(() => {
app.whenReady().then(async () => {
if (isDev) {
// Wait for the dev server to be ready
await waitForDevServer()
}

mainWindow.set(createWindow('main')) // Create main window

// macOS: Recreate a window if none are open but the dock icon is activated
Expand Down
79 changes: 39 additions & 40 deletions public/app/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,64 @@
* @author jalenng
*/

const path = require('path')
const path = require("path");

const { app, nativeTheme, nativeImage, Menu, Tray } = require('electron')
const { app, nativeTheme, nativeImage, Menu, Tray } = require("electron");

const { appName, isMacOS } = require('../constants')
const { timerSystem } = require('../systems/systems')
const createWindow = require('./createWindow')
const { mainWindow, windowStillExists } = require('./windowManager')
const { appName, isMacOS } = require("../constants");
const { timerSystem } = require("../systems/systems");
const createWindow = require("./createWindow");
const { mainWindow, windowStillExists } = require("./windowManager");

const contextMenu = Menu.buildFromTemplate([
{ label: appName, enabled: false },
{ type: 'separator' },
{ label: 'Quit', click: app.exit }
])
{ label: appName, click: showMainWindow },
{ type: "separator" },
{ label: "Quit", click: app.exit },
]);

/**
* Determines the path of the tray icon image based on the OS and timer status.
* @returns {String} the path of the image file to use
*/
function getTrayImage () {
function getTrayImage() {
// Calculate percentage
const timerStatus = timerSystem.getStatus()
const percentage = timerStatus.remainingTime / timerStatus.totalDuration * 100
const percentageMultOfFive = Math.round(percentage / 5) * 5
const timerStatus = timerSystem.getStatus();
const percentage = (timerStatus.remainingTime / timerStatus.totalDuration) * 100;
const percentageMultOfFive = Math.round(percentage / 5) * 5;

const folder = isMacOS
? 'template'
: nativeTheme.shouldUseDarkColors
? 'white'
: 'black'
const folder = isMacOS ? "template" : nativeTheme.shouldUseDarkColors ? "white" : "black";

const filename = isMacOS
? `${percentageMultOfFive}Template.png`
: `${percentageMultOfFive}.png`
const imagePath = path.join(__dirname, '..', '..', 'tray_assets', folder, filename)
const image = nativeImage.createFromPath(imagePath)
const filename = isMacOS ? `${percentageMultOfFive}Template.png` : `${percentageMultOfFive}.png`;
const imagePath = path.join(__dirname, "..", "..", "tray_assets", folder, filename);
const image = nativeImage.createFromPath(imagePath);

return image
return image;
}

function createTray () {
const tray = new Tray(getTrayImage())
tray.setToolTip(appName)
tray.setContextMenu(contextMenu)
tray.on('click', () => {
if (windowStillExists(mainWindow.get())) {
mainWindow.get().show()
} else {
mainWindow.set(createWindow('main'))
}
})
function createTray() {
const tray = new Tray(getTrayImage());
tray.setToolTip(appName);
tray.setContextMenu(contextMenu);
tray.on("click", () => {
showMainWindow();
});

// Update system tray icon on an interval
setInterval(() => {
tray.setImage(getTrayImage())
}, 5000)
tray.setImage(getTrayImage());
}, 5000);
}

function showMainWindow() {
if (windowStillExists(mainWindow.get())) {
mainWindow.get().show();
} else {
mainWindow.set(createWindow("main"));
mainWindow.get().show();
}
}

/** Export tray reference and function to create tray */
module.exports = {
createTray
}
createTray,
};
23 changes: 23 additions & 0 deletions public/app/waitForDevServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { net } = require('electron')

function waitForDevServer (port = 3000) {
return new Promise((resolve) => {
const tryConnection = () => {
const request = net.request(`http://localhost:${port}`)
request.on('response', (response) => {
if (response.statusCode === 200) {
resolve()
} else {
setTimeout(tryConnection, 1000)
}
})
request.on('error', () => {
setTimeout(tryConnection, 1000)
})
request.end()
}
tryConnection()
})
}

module.exports = waitForDevServer
Binary file added public/assets/fonts/fabric-icons-0-467ee27f.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-1-4d521695.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-10-c4ded8e4.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-11-2a8393d6.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-12-7e945a1e.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-13-c3989a02.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-14-5cf58db8.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-15-3807251b.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-16-9cf93f3b.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-17-0c4ed701.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-2-63c99abf.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-3-089e217a.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-4-a656cc0a.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-5-f95ba260.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-6-ef6fd590.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-7-2b97bb99.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-8-6fdf1528.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-9-c6162b42.woff
Binary file not shown.
Binary file added public/assets/fonts/fabric-icons-a13498cf.woff
Binary file not shown.
13 changes: 13 additions & 0 deletions public/ipc/systemsIPC.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,16 @@ ipcMain.on('get-blockers', (event) => {
ipcMain.handle('clear-blockers', () => {
blockerSystem.clear()
})

ipcMain.handle('cancel-break', () => {
breakSystem.end()
})

ipcMain.handle('delay-break', (event, seconds) => {
timerSystem.setDurationOverride(seconds * 1000)
breakSystem.end()
})

ipcMain.handle('trigger-break', () => {
timerSystem.end()
})
16 changes: 13 additions & 3 deletions public/store/storeSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
* enableSound (boolean)
* interval (number)
* sound (string)
* snoozeSeconds (number)
- blockers
* apps (array of strings)
* blockOnBattery (boolean)
- startup
* startAppOnLogin (boolean)
* startTimerOnAppStartup (boolean)
* hideOnAppStartup (boolean)
* autoRestartTimer (boolean)
- appearance
* theme (string)
* alwaysOnTop (boolean)
Expand All @@ -27,7 +29,8 @@ const preferencesSchema = {
enableSound: true,
interval: 20,
sound: 'Long Expected.mp3',
soundVolume: 100
soundVolume: 100,
snoozeSeconds: 120
},
blockers: {
apps: [],
Expand All @@ -36,7 +39,8 @@ const preferencesSchema = {
startup: {
startAppOnLogin: true,
startTimerOnAppStartup: true,
hideOnAppStartup: false
hideOnAppStartup: false,
autoRestartTimer: true
},
appearance: {
theme: 'system',
Expand All @@ -63,6 +67,11 @@ const preferencesSchema = {
type: 'number',
minimum: 0,
maximum: 100
},
snoozeSeconds: {
type: 'number',
minimum: 10,
maximum: 300
}
},
additionalProperties: false
Expand All @@ -85,7 +94,8 @@ const preferencesSchema = {
properties: {
startAppOnLogin: { type: 'boolean' },
startTimerOnAppStartup: { type: 'boolean' },
hideOnAppStartup: { type: 'boolean' }
hideOnAppStartup: { type: 'boolean' },
autoRestartTimer: { type: 'boolean' }
},
additionalProperties: false
},
Expand Down
Loading