diff --git a/package.json b/package.json index 434e8ff1..08d08cf1 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "webpack-merge": "^5.9.0" }, "build": { - "buildVersion": "34", + "buildVersion": "35", "asar": true, "asarUnpack": "**\\*.{node,dll}", "files": [ diff --git a/src/main/main.tsx b/src/main/main.tsx index 439019f8..c3d5187d 100644 --- a/src/main/main.tsx +++ b/src/main/main.tsx @@ -168,7 +168,7 @@ const handleWindowAllClosed = () => { const tray = configStorage.get('tray'); if(process.platform !== 'darwin' && !tray) { app.quit(); - } else if(!process.mas && process.platform === 'darwin' && tray) { + } else if(process.platform === 'darwin' && tray) { app.dock?.hide(); } else { mainWindow?.hide(); diff --git a/src/main/modules/Menu.tsx b/src/main/modules/Menu.tsx index 02a6b489..c0af6ee5 100644 --- a/src/main/modules/Menu.tsx +++ b/src/main/modules/Menu.tsx @@ -1,6 +1,6 @@ import { app, Menu, dialog, shell } from 'electron'; import { setFile } from './File/File'; -import { mainWindow } from '../main'; +import { mainWindow, handleCreateWindow } from '../main'; import { openFile, createFile } from './File/Dialog'; import { handleRequestArchive } from './File/Archive'; import { configStorage, filterStorage } from '../config'; @@ -88,13 +88,7 @@ function createMenu(files: FileObject[]) { setFile(index); }, })) - : []), - { type: 'separator' }, - { - label: 'Close window', - accelerator: 'CmdOrCtrl+W', - role: 'close', - }, + : []) ], }, { @@ -112,7 +106,8 @@ function createMenu(files: FileObject[]) { { label: 'View', submenu: [ - { + ...(files?.length > 0 + ? [{ label: 'Toggle drawer', accelerator: 'CmdOrCtrl+B', click: () => { @@ -120,6 +115,14 @@ function createMenu(files: FileObject[]) { configStorage.set('isDrawerOpen', !isDrawerOpen); }, }, + { + label: 'Toggle file tabs', + click: () => { + const showFileTabs = configStorage.get('showFileTabs'); + configStorage.set('showFileTabs', !showFileTabs); + }, + }] + : []), { label: 'Toggle navigation', accelerator: 'Ctrl+Alt+H', @@ -128,13 +131,6 @@ function createMenu(files: FileObject[]) { configStorage.set('isNavigationOpen', !isNavigationOpen); }, }, - { - label: 'Toggle file tabs', - click: () => { - const showFileTabs = configStorage.get('showFileTabs'); - configStorage.set('showFileTabs', !showFileTabs); - }, - }, { label: 'Toggle theme', accelerator: 'Ctrl+Alt+D', @@ -145,51 +141,70 @@ function createMenu(files: FileObject[]) { }, ], }, + ...(files?.length > 0 + ? [{ + label: 'Todos', + submenu: [ + { + label: 'Add new todo', + accelerator: 'CmdOrCtrl+N', + click: () => { + mainWindow?.webContents.send('isDialogOpen'); + }, + }, + { + label: 'Find', + accelerator: 'CmdOrCtrl+F', + click: () => { + const isSearchOpen = configStorage.get('isSearchOpen'); + configStorage.set('isSearchOpen', !isSearchOpen); + }, + }, + { + label: 'Toggle completed', + accelerator: 'Ctrl+H', + click: async () => { + const showCompleted = configStorage.get('showCompleted'); + configStorage.set('showCompleted', !showCompleted); + }, + }, + { + label: 'Reset filters', + accelerator: 'CmdOrCtrl+0', + click: async () => { + filterStorage.set('filters', {}); + }, + }, + { + label: 'Archive completed todos', + accelerator: 'Ctrl+Alt+A', + click: () => { + handleRequestArchive(); + }, + }, + { + role: 'reload', + visible: false, + }, + ], + }] + : []), { - label: 'Todos', + label: 'Window', submenu: [ { - label: 'Add new todo', - accelerator: 'CmdOrCtrl+N', - click: () => { - mainWindow!.webContents.send('isDialogOpen'); - }, - }, - { - label: 'Find', - accelerator: 'CmdOrCtrl+F', - click: () => { - const isSearchOpen = configStorage.get('isSearchOpen'); - configStorage.set('isSearchOpen', !isSearchOpen); - }, - }, - { - label: 'Toggle completed', - accelerator: 'Ctrl+H', - click: async () => { - const showCompleted = configStorage.get('showCompleted'); - configStorage.set('showCompleted', !showCompleted); - }, - }, - { - label: 'Reset filters', - accelerator: 'CmdOrCtrl+0', - click: async () => { - filterStorage.set('filters', {}); - }, + label: 'Close window', + accelerator: 'CmdOrCtrl+W', + role: 'close', }, + { type: 'separator' }, { - label: 'Archive completed todos', - accelerator: 'Ctrl+Alt+A', + label: 'sleek', click: () => { - handleRequestArchive(); - }, - }, - { - role: 'reload', - visible: false, - }, - ], + handleCreateWindow(); + } + } + ] }, { label: 'Help', diff --git a/src/renderer/App.scss b/src/renderer/App.scss index dec5d87b..ab896018 100644 --- a/src/renderer/App.scss +++ b/src/renderer/App.scss @@ -27,7 +27,6 @@ body { display: flex; flex-direction: row; margin-left: 5em; - transition: margin-left 0.3s ease; header { height: 2.5em; display: flex; @@ -45,6 +44,9 @@ body { left: -5em; } } + &.hideNavigation.disableAnimations { + transition: none; + } &.darkTheme { button.showNavigation { background: $darker-grey; diff --git a/src/renderer/Dialog/AutoSuggest.tsx b/src/renderer/Dialog/AutoSuggest.tsx index b06caa77..9229decd 100644 --- a/src/renderer/Dialog/AutoSuggest.tsx +++ b/src/renderer/Dialog/AutoSuggest.tsx @@ -124,11 +124,6 @@ const AutoSuggest: React.FC = ({ onKeyDown: handleKeyDown, 'test-id': 'dialog-autosuggest-textfield', }; - - const containerStyle = { - width: textFieldRef.current?.offsetWidth + 28 || 'auto', - }; - useEffect(() => { textFieldRef.current?.focus(); }, [textFieldRef]); @@ -140,7 +135,7 @@ const AutoSuggest: React.FC = ({ )} renderSuggestionsContainer={({ containerProps, children }) => ( - + {children} )} diff --git a/src/renderer/Dialog/DatePicker.tsx b/src/renderer/Dialog/DatePicker.tsx index 8e9ee194..be94ed3e 100644 --- a/src/renderer/Dialog/DatePicker.tsx +++ b/src/renderer/Dialog/DatePicker.tsx @@ -6,14 +6,11 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import dayjs from 'dayjs'; import './DatePicker.scss'; -const { ipcRenderer } = window.api; - interface Props extends WithTranslation { date: string | null; type: string; settings: Settings; - textFieldValue: string; - todoObject: TodoObject | null; + handleChange: function; t: typeof i18n.t; } @@ -21,20 +18,9 @@ const DatePickerComponent: React.FC = ({ date, type, settings, - textFieldValue, - todoObject, + handleChange, t, }) => { - - const handleChange = (date: dayjs.Dayjs | null) => { - try { - const dateString = (date) ? dayjs(date).format('YYYY-MM-DD') : null; - ipcRenderer.send('updateTodoObject', todoObject?.id, textFieldValue, type, dateString); - } catch(error: any) { - console.error(error); - } - }; - return ( = ({ format="YYYY-MM-DD" label={t(`todoDialog.datePicker.${type}`)} value={date ? dayjs(date) : null} - onChange={(date) => handleChange(date)} + onChange={(date) => handleChange(type, date)} // data-testid={`dialog-picker-date-${type}`} // InputProps={{ 'data-testid': `dialog-picker-date-${type}` }} slotProps={{ diff --git a/src/renderer/Dialog/Dialog.tsx b/src/renderer/Dialog/Dialog.tsx index 04ef5e31..9cd29a70 100644 --- a/src/renderer/Dialog/Dialog.tsx +++ b/src/renderer/Dialog/Dialog.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, memo } from 'react'; import { Button, Dialog, DialogContent, DialogActions, AlertColor } from '@mui/material'; import { withTranslation, WithTranslation } from 'react-i18next'; +import dayjs from 'dayjs'; import AutoSuggest from './AutoSuggest'; import PriorityPicker from './PriorityPicker'; import DatePicker from './DatePicker'; @@ -87,7 +88,23 @@ const DialogComponent: React.FC = memo(({ setRecurrence(todoObject?.rec || null); setPomodoro(todoObject?.pm || 0); } - } + }; + + const handleChange = (type: string, value: string) => { + try { + let updatedValue; + if(type === 'due' || type === 't') { + updatedValue = (value) ? dayjs(value).format('YYYY-MM-DD') : null; + } else if(type === 'pm') { + updatedValue = (value === '0') ? null : value; + } else { + updatedValue = value; + } + ipcRenderer.send('updateTodoObject', todoObject?.id, textFieldValue, type, updatedValue); + } catch(error: any) { + console.error(error); + } + }; useEffect(() => { if(todoObject) { @@ -129,32 +146,27 @@ const DialogComponent: React.FC = memo(({ /> diff --git a/src/renderer/Dialog/PomodoroPicker.tsx b/src/renderer/Dialog/PomodoroPicker.tsx index c9b92d19..bdd6d7cf 100644 --- a/src/renderer/Dialog/PomodoroPicker.tsx +++ b/src/renderer/Dialog/PomodoroPicker.tsx @@ -3,36 +3,22 @@ import { FormControl, TextField } from '@mui/material'; import { ReactComponent as TomatoIconDuo } from '../../../assets/icons/tomato-duo.svg' import './PomodoroPicker.scss'; -const { ipcRenderer } = window.api; - -interface Props { +interface PomodoroPickerProps { pomodoro: number | string; - textFieldValue: string; - todoObject: TodoObject | null; + handleChange: function; } -const PomodoroPicker: React.FC = ({ +const PomodoroPicker: React.FC = ({ pomodoro, - textFieldValue, - todoObject, + handleChange, }) => { - - const handleChange = (event: React.ChangeEvent) => { - try { - const value = (event.target.value === '0') ? null : event.target.value; - ipcRenderer.send('updateTodoObject', todoObject?.id, textFieldValue, 'pm', value); - } catch(error: any) { - console.error(error); - } - }; - return ( } type="number" - onChange={handleChange} + onChange={(event) => handleChange('pm', event.target.value)} value={pomodoro} data-testid="dialog-picker-pomodoro" inputProps={{ diff --git a/src/renderer/Dialog/PriorityPicker.tsx b/src/renderer/Dialog/PriorityPicker.tsx index 93a982b9..2f8739e8 100644 --- a/src/renderer/Dialog/PriorityPicker.tsx +++ b/src/renderer/Dialog/PriorityPicker.tsx @@ -4,32 +4,20 @@ import { withTranslation, WithTranslation } from 'react-i18next'; import { i18n } from '../Settings/LanguageSelector'; import './PriorityPicker.scss'; -const { ipcRenderer } = window.api; const alphabetArray = Array.from({ length: 26 }, (_, index) => String.fromCharCode(65 + index)); const priorities = [{ value: '-', label: '-' }, ...alphabetArray.map((letter) => ({ value: letter, label: letter }))]; -interface Props extends WithTranslation { +interface PriorityPickerProps extends WithTranslation { priority: string; - textFieldValue: string; - todoObject: TodoObject | null; + handleChange: function; t: typeof i18n.t; } -const PriorityPicker: React.FC = ({ +const PriorityPicker: React.FC = ({ priority, - textFieldValue, - todoObject, + handleChange, t, -}: Props) => { - - const handleChange = (event: SelectChangeEvent) => { - try { - ipcRenderer.send('updateTodoObject', todoObject?.id, textFieldValue, 'priority', event.target.value); - } catch(error: any) { - console.error(error); - } - }; - +}) => { return ( {t('todoDialog.priorityPicker.label')} @@ -37,7 +25,7 @@ const PriorityPicker: React.FC = ({ id="priorityPicker" label={t('todoDialog.priorityPicker.label')} value={priority} - onChange={(event: SelectChangeEvent) => handleChange(event)} + onChange={(event: SelectChangeEvent) => handleChange('priority', event.target.value)} data-testid="dialog-picker-priority" > {priorities.map((priorityOption) => ( diff --git a/src/renderer/Dialog/RecurrencePicker.tsx b/src/renderer/Dialog/RecurrencePicker.tsx index 7556db5b..3b4ac915 100644 --- a/src/renderer/Dialog/RecurrencePicker.tsx +++ b/src/renderer/Dialog/RecurrencePicker.tsx @@ -6,31 +6,15 @@ import { withTranslation, WithTranslation } from 'react-i18next'; import { i18n } from '../Settings/LanguageSelector'; import './RecurrencePicker.scss'; -const { ipcRenderer } = window.api; - -const getInterval = (recurrence: string | null) => { - return recurrence && recurrence.startsWith('+') ? recurrence.slice(2, 3) : recurrence ? recurrence.slice(1, 2) : null; -} - -const getAmount = (recurrence: string | null) => { - return recurrence && recurrence.startsWith('+') ? recurrence.slice(1, 2) : recurrence ? recurrence.slice(0, 1) : null; -} - -const getStrictIndicator = (recurrence: string | null) => { - return recurrence ? recurrence.startsWith('+') : false; -} - -interface Props extends WithTranslation { +interface RecurrencePickerProps extends WithTranslation { recurrence: string | null; - textFieldValue: string; - todoObject: TodoObject | null; + handleChange: function; t: typeof i18n.t; } -const RecurrencePicker: React.FC = ({ +const RecurrencePicker: React.FC = ({ recurrence, - textFieldValue, - todoObject, + handleChange, t }) => { const recurrenceFieldRef = useRef(null); @@ -38,15 +22,6 @@ const RecurrencePicker: React.FC = ({ const [interval, setInterval] = useState(null); const [amount, setAmount] = useState(null); - const handleChange = (recurrence: string | null, event?: React.ChangeEvent | undefined) => { - try { - event?.preventDefault(); - ipcRenderer.send('updateTodoObject', todoObject?.id, textFieldValue, 'rec', recurrence); - } catch(error: any) { - console.error(error); - } - }; - const handleIntervalChange = (event: React.ChangeEvent) => { if(!amount) setAmount('1'); setInterval(event.target.value); @@ -65,11 +40,20 @@ const RecurrencePicker: React.FC = ({ useEffect(() => { if(amount && interval) { const updatedValue = strictRecurrence ? '+' + amount + interval : amount + interval; - handleChange(updatedValue); + handleChange('rec', updatedValue); } }, [interval, amount, strictRecurrence]); useEffect(() => { + const getInterval = (recurrence: string | null) => { + return recurrence && recurrence.startsWith('+') ? recurrence.slice(2, 3) : recurrence ? recurrence.slice(1, 2) : null; + } + const getAmount = (recurrence: string | null) => { + return recurrence && recurrence.startsWith('+') ? recurrence.slice(1, 2) : recurrence ? recurrence.slice(0, 1) : null; + } + const getStrictIndicator = (recurrence: string | null) => { + return recurrence ? recurrence.startsWith('+') : false; + } setStrictRecurrence(getStrictIndicator(recurrence)); setInterval(getInterval(recurrence)); setAmount(getAmount(recurrence)); @@ -103,7 +87,7 @@ const RecurrencePicker: React.FC = ({ handleChange(recurrence)} + onChange={() => handleChange('rec', recurrence)} value={recurrence || '-'} inputRef={recurrenceFieldRef} data-testid="dialog--picker-recurrence" diff --git a/src/renderer/Matomo.tsx b/src/renderer/Matomo.tsx index 6fee1b41..05b78236 100644 --- a/src/renderer/Matomo.tsx +++ b/src/renderer/Matomo.tsx @@ -12,7 +12,7 @@ const MatomoComponent: React.FC = ({ useEffect(() => { const anonymousUserId = (environment === 'development') ? 'Dev' : settings.anonymousUserId; if(anonymousUserId) { - const matomoContainer: string = (environment === 'development') ? 'https://www.datenkrake.eu/matomo/js/container_WVsEueTV_dev_a003c77410fd43f247329b3b.js' : 'https://www.datenkrake.eu/matomo/js/container_WVsEueTV.js'; + const matomoContainer: string = (environment === 'development') ? 'https://www.datenkrake.eu/matomo5/js/container_WVsEueTV_dev_a003c77410fd43f247329b3b.js' : 'https://www.datenkrake.eu/matomo5/js/container_WVsEueTV.js'; const _mtm = window._mtm = window._mtm || []; const _paq = window._paq || []; diff --git a/src/renderer/Navigation.scss b/src/renderer/Navigation.scss index a2e6d4ab..a8c0f5b7 100644 --- a/src/renderer/Navigation.scss +++ b/src/renderer/Navigation.scss @@ -6,6 +6,7 @@ border-top-right-radius: $radius; } +.disableAnimations.hideNavigation #navigation, .disableAnimations #navigation { transition: none; button.showNavigation {