diff --git a/src/content/mytinytodo.js b/src/content/mytinytodo.js index 3fe78f4..b21ad0c 100644 --- a/src/content/mytinytodo.js +++ b/src/content/mytinytodo.js @@ -79,6 +79,7 @@ var mytinytodo = window.mytinytodo = _mtt = { markdown: true, viewTaskOnClick: false, newTaskCounter: false, + newTaskCounterIcon: false, }, timers: { @@ -725,6 +726,10 @@ var mytinytodo = window.mytinytodo = _mtt = { // Counter if (this.options.newTaskCounter) { this.addAction('listsLoaded', newTaskCounterStart); + this.addAction('listSelected', newTaskCounterOnListSelected) + if (this.options.newTaskCounterIcon) { + this.addAction('newTaskCounterUpdated', newTaskCounterUpdated); + } } this.doAction( 'init' ); @@ -1182,6 +1187,7 @@ function loadTasks(opts) }); curList.lastTime = json.time; setNewTaskCounterForList(curList.id, 0); + _mtt.doAction("newTaskCounterUpdated", curList.id); if(opts.beforeShow && opts.beforeShow.call) { opts.beforeShow(); } @@ -1623,7 +1629,10 @@ function tabSelect(elementOrId) if (id == -1) $('#mtt').addClass('show-all-tasks'); else $('#mtt').removeClass('show-all-tasks'); if (filter.search != '') liveSearchToggle(0, 1); - mytinytodo.doAction('listSelected', tabLists.get(id)); + mytinytodo.doAction('listSelected', { + 'list': curList, + 'prevList':prevList + }); } const newTitle = curList.name + ' - ' + _mtt.options.title; const isFirstLoad = flag.firstLoad; @@ -2059,6 +2068,7 @@ function submitFullTask(form) var item = json.list[0]; taskList[item.id] = item; taskOrder.push(parseInt(item.id)); + curList.lastTaskCreatedTime = item.dateInt; $('#tasklist').append(_mtt.prepareTaskStr(item)); changeTaskOrder(item.id); _mtt.pageBack(); @@ -2471,8 +2481,9 @@ function cmenuOnListRenamed(list) $('#cmenu_list\\:'+list.id).text(list.name); }; -function cmenuOnListSelected(list) +function cmenuOnListSelected(a) { + const list = a.list; $('#cmenulistscontainer li').removeClass('mtt-item-disabled'); $('#cmenu_list\\:'+list.id).addClass('mtt-item-disabled').removeClass('mtt-list-hidden'); }; @@ -2490,8 +2501,9 @@ function cmenuOnListHidden(list) }; -function tabmenuOnListSelected(list) +function tabmenuOnListSelected(a) { + const list = a.list; if (list.published) { $('#btnPublish').addClass('mtt-item-checked'); $('#btnRssFeed').removeClass('mtt-item-disabled'); @@ -2629,8 +2641,9 @@ function slmenuOnListAdded(list) $('#slmenucontainer ul').append('
  • '+list.name+'
  • '); }; -function slmenuOnListSelected(list) +function slmenuOnListSelected(a) { + const list = a.list; $('#slmenucontainer li').removeClass('mtt-item-checked'); $('#slmenucontainer li.list-id-'+list.id).addClass('mtt-item-checked').removeClass('mtt-list-hidden'); @@ -2768,19 +2781,108 @@ function newTaskCounter() setNewTaskCounterForList(list.id, counters[list.id]); } }); + _mtt.doAction("newTaskCounterUpdated"); } }); } function setNewTaskCounterForList(listId, counter) { + const list = tabLists.get(listId); + if (!list) return; + if (list.newTaskCounterOld) { + counter += list.newTaskCounterOld; + } if (counter > 0) { $('#list_' + listId).find('.counter').text(counter).removeClass('hidden'); + list.newTaskCounter = counter; } else { $('#list_' + listId).find('.counter').text('').addClass('hidden'); + list.newTaskCounter = 0; } } +function newTaskCounterOnListSelected(a) +{ + if (a.prevList && a.prevList.newTaskCounter) { + a.prevList.newTaskCounterOld = a.prevList.newTaskCounter; + } +} + +/** + * Set favicon with number of new tasks + */ +function newTaskCounterUpdated() +{ + // Calc a total number of new tasks in visible tabs + let total = 0; + tabLists.getAll().forEach( (list) => { + if (list.newTaskCounter && (!list.hidden || list.id != -1)) { + total += list.newTaskCounter; + } + }); + + // Restore original icon + if (total <= 0) { + const o = document.querySelectorAll('link[rel="icon"]'); + let oType, oHref; + for (let i = 0; i < o.length; i++) { + if (o[i].dataset.ohref) { + oHref = o[i].dataset.ohref; + oType = o[i].dataset.otype; + o[i].parentNode.removeChild(o[i]); + } + } + if (oHref) { + const n = document.createElement('link'); + n.setAttribute('rel', 'icon'); + n.setAttribute('href', oHref); + n.setAttribute('type', oType); + document.querySelector('head').appendChild(n); + } + return; + } + + // Draw new icon + const c = document.createElement('canvas'); + c.height = c.width = 64; + const ctx = c.getContext('2d'); + //filled circle in center + ctx.lineWidth = 4; + ctx.fillStyle = '#ff0000'; + ctx.beginPath(); + ctx.arc(c.width / 2, c.height / 2, c.width / 2 - 2, 0, 2 * Math.PI, false); + ctx.fill(); + //number in center + ctx.font = '48px Helvetica'; + ctx.fillStyle = '#ffffff'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText( total > 9 ? '9+' : total, 32, 32, 50); + + // Save params of original icon + const o = document.querySelectorAll('link[rel="icon"]'); + let oType, oHref; + for (let i = 0; i < o.length; i++) { + oHref = o[i].dataset.ohref; + oType = o[i].dataset.otype; + if (!oHref) { + oHref = o[i].getAttribute('href'); + oType = o[i].getAttribute('type') || ''; + } + o[i].parentNode.removeChild(o[i]); + } + // Set new icon + const n = document.createElement('link'); + n.setAttribute('rel', 'icon'); + n.setAttribute('href', c.toDataURL()); //"data:image/png;base64,......" + if (oHref) { + n.dataset.ohref = oHref; + n.dataset.otype = oType; + } + document.querySelector('head').appendChild(n); +} + /* Errors and Info messages */ diff --git a/src/includes/api/TasksController.php b/src/includes/api/TasksController.php index 2f0d6cb..0a5bc14 100644 --- a/src/includes/api/TasksController.php +++ b/src/includes/api/TasksController.php @@ -272,6 +272,7 @@ class TasksController extends ApiController { $db = DBConnection::instance(); $a = []; + $time = time(); if ($sqlWhereList) { $sqlWhere = implode(' OR ', $sqlWhereList); @@ -300,7 +301,8 @@ class TasksController extends ApiController { 'ok' => true, 'total' => count($b) + count($a), 'tasks' => $b, - 'lists' => $a + 'lists' => $a, + 'time' => $time ]; } diff --git a/src/includes/class.config.php b/src/includes/class.config.php index 9e1d11d..2446656 100644 --- a/src/includes/class.config.php +++ b/src/includes/class.config.php @@ -91,7 +91,8 @@ class Config 'appearance' => array('default'=>'system', 'type'=>'s', 'options'=>array('system','light','dark')), # New tasks counter - 'newTaskCounter' => array('default' => 0, 'type'=>'i'), + 'newTaskCounter' => array('default' => 0, 'type'=>'i'), + 'newTaskCounterIcon' => array('default' => 0, 'type'=>'i'), # Array of activated extensions 'extensions' => array('default'=>[], 'type'=>'a') diff --git a/src/includes/lang/en.json b/src/includes/lang/en.json index d481a1e..04e11e9 100644 --- a/src/includes/lang/en.json +++ b/src/includes/lang/en.json @@ -16,9 +16,9 @@ "advanced_add": "Advanced", "searching": "Searching for", "tasks": "Tasks", - "taskdate_inline_created": "created at %s", - "taskdate_inline_edited": "edited at %s", - "taskdate_inline_completed": "completed at %s", + "taskdate_inline_created": "created on %s", + "taskdate_inline_edited": "edited on %s", + "taskdate_inline_completed": "completed on %s", "taskdate_inline_duedate": "Due %s", "taskdate_created": "Created", "taskdate_edited": "Last edited", @@ -191,6 +191,7 @@ "set_appearance_dark": "Dark", "set_newtaskcounter_h": "New tasks counter", "set_newtaskcounter": "Check for new tasks", + "set_newtaskcountericon": "Show counter in favicon", "set_extensions": "Extensions", "set_activate": "Activate", "set_deactivate": "Deactivate", diff --git a/src/includes/lang/ru.json b/src/includes/lang/ru.json index fa123c7..d3d95b5 100644 --- a/src/includes/lang/ru.json +++ b/src/includes/lang/ru.json @@ -191,6 +191,7 @@ "set_appearance_dark": "Тёмная", "set_newtaskcounter_h": "Счетчик новых задач", "set_newtaskcounter": "Проверять наличие новых задач", + "set_newtaskcountericon": "Показывать счетчик в favicon", "set_extensions": "Дополнения", "set_activate": "Активировать", "set_deactivate": "Деактивировать", diff --git a/src/index.php b/src/index.php index 8675753..40b0205 100644 --- a/src/index.php +++ b/src/index.php @@ -96,8 +96,13 @@ function js_options() "autotag" => Config::get('autotag') ? true : false, "markdown" => Config::get('markup') == 'v1' ? false : true, "newTaskCounter" => Config::get('newTaskCounter') ? true : false, + "newTaskCounterIcon" => Config::get('newTaskCounterIcon') ? true : false, ); - $json = json_encode($a, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); + $flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE; + if (MTT_DEBUG) { + $flags |= JSON_PRETTY_PRINT; + } + $json = json_encode($a, $flags); if ($json === false) { error_log("MTT Error: Failed to encode array of options to JSON. Code: ". (int)json_last_error()); echo "{}"; diff --git a/src/settings.php b/src/settings.php index 6c34fbd..b6b1941 100644 --- a/src/settings.php +++ b/src/settings.php @@ -56,6 +56,7 @@ if(isset($_POST['save'])) Config::set('exactduedate', (int)_post('exactduedate')); Config::set('appearance', removeNewLines(trim(_post('appearance'))) ); Config::set('newTaskCounter', (int)_post('newTaskCounter')); + Config::set('newTaskCounterIcon', (int)_post('newTaskCounterIcon')); Config::save(); $t['saved'] = 1; jsonExit($t); @@ -387,7 +388,10 @@ header('Content-type:text/html; charset=utf-8');
    :
    -
    +
    +
    + +