+ add badge to tabs with counter of newly created tasks (GH-5, GH-25)

This commit is contained in:
maxpozdeev 2023-09-08 08:09:00 +03:00
parent f870bc3cf0
commit 42e265d802
5 changed files with 174 additions and 14 deletions

View file

@ -45,6 +45,9 @@ $endpoints = array(
'/tasks/parseTitle' => [
'POST' => [ TasksController::class , 'postTitleParse' ],
],
'/tasks/newCounter' => [
'POST' => [ TasksController::class , 'postNewCounter' ],
],
'/tagCloud/(-?\d+)' => [
'GET' => [ TagsController::class , 'getCloud' ],
],

View file

@ -30,6 +30,7 @@ var tabLists = {
_length: 0,
_order: [],
_alltasks: {},
lastTime: 0,
clear: function(){
this._lists = {}; this._length = 0; this._order = [];
this._alltasks = { id:-1, showCompl:0, sort:3, name:_mtt.lang.get('alltasks') };
@ -76,11 +77,13 @@ var mytinytodo = window.mytinytodo = _mtt = {
history: true,
markdown: true,
viewTaskOnClick: false,
newTaskCounter: true,
},
timers: {
previewtag: 0,
ajaxAnimation: 0,
newTaskCounter: 0,
},
lang: {
@ -719,6 +722,11 @@ var mytinytodo = window.mytinytodo = _mtt = {
});
}
// Counter
if (this.options.newTaskCounter) {
this.addAction('listsLoaded', newTaskCounterStart);
}
this.doAction( 'init' );
return this;
@ -804,8 +812,10 @@ var mytinytodo = window.mytinytodo = _mtt = {
if (list) {
openListId = list.id;
}
tabLists.lastTime = res.time;
res.list.forEach( (item) => {
item.lastTime = res.time;
if ( item.id == -1 ) {
tabLists._alltasks = item;
ti += prepareListHtml(item);
@ -1070,7 +1080,7 @@ function renameCurList()
var item = json.list[0];
curList = item;
tabLists.replace(item);
$('#lists ul>.mtt-tab-selected>a').attr('title', item.name).find('span').html(item.name); //FIXME: wft?
$('#list_'+curList.id).replaceWith(prepareListHtml(curList, true));
mytinytodo.doAction('listRenamed', item);
});
});
@ -1164,6 +1174,8 @@ function loadTasks(opts)
taskOrder.push(parseInt(item.id));
changeTaskCnt(item, 1);
});
curList.lastTime = json.time;
$('#list_'+curList.id).find('.counter').text('').addClass('hidden');
if(opts.beforeShow && opts.beforeShow.call) {
opts.beforeShow();
}
@ -1172,18 +1184,22 @@ function loadTasks(opts)
});
};
function prepareListHtml(list)
function prepareListHtml(list, isSelected)
{
var opentag = '';
const selected = isSelected ? ' mtt-tab-selected' : '';
let opentag = '';
if (list.id == -1) {
opentag = '<li id="list_all" class="mtt-tab' + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
opentag = '<li id="list_all" class="mtt-tab' + selected + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
}
else {
opentag = '<li id="list_' + list.id + '" class="mtt-tab' + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
opentag = '<li id="list_' + list.id + '" class="mtt-tab' + selected + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
}
return opentag +
'<a href="' + _mtt.urlForList(list) + '" title="' + list.name + '"><span>' + list.name + '</span>' +
'<div class="list-action mtt-img-button"><span></span></div></a></li>';
'<a href="' + _mtt.urlForList(list) + '" title="' + list.name + '">'+
'<span class="counter hidden"></span>'+
'<span class="title">' + list.name + '</span>' +
'<div class="list-action mtt-img-button"><span></span></div>'+
'</a></li>';
}
function prepareTaskStr(item, noteExp)
@ -2680,6 +2696,63 @@ function setLocalStorageItem(key, value)
}
}
function newTaskCounterStart()
{
clearInterval(_mtt.timers.newTaskCounter);
_mtt.timers.newTaskCounter = setInterval(newTaskCounter, 60*1000); //every 60 sec
}
function newTaskCounter()
{
const params = {
list: curList.id,
later: curList.lastTime,
lists: [],
};
tabLists.getAll().forEach( (list) => {
if (list.hidden || list.id == -1 || list.id == curList.id) {
return;
}
params.lists.push({
listId: list.id,
later: list.lastTime
});
});
fetch(_mtt.apiUrl + 'tasks/newCounter', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'MTT-Token': _mtt.options.token,
},
body: JSON.stringify(params)
})
.then(response => response.json())
.then(json => {
if (json && json.total) {
if (Array.isArray(json.tasks)) {
let curCounter = 0;
json.tasks.forEach((id) => {
if (taskList[id]) {
console.log('skip existing id ', id);
}
else {
curCounter++;
}
});
if (curCounter > 0) {
$('#list_'+curList.id).find('.counter').text(curCounter).removeClass('hidden');
}
}
if (Array.isArray(json.lists)) {
json.lists.forEach((item) => {
$('#list_'+item.listId).find('.counter').text(item.counter).removeClass('hidden');
});
}
}
});
}
/*
Errors and Info messages
*/

View file

@ -286,25 +286,30 @@ select.form-input {
border-bottom:none;
border-top-right-radius:8px;
transition:background-color 0.1s ease-in;
max-width:230px;
min-width:95px;
}
.mtt-tab a {
margin:0; text-decoration:none; white-space:nowrap;
color: var(--color-tab-text);
display:inline-block; outline:none;
box-sizing:border-box; height:2.2rem; padding:1px 0.3rem 0 0.3rem; display:flex; align-items:center;
box-sizing:border-box;
height:2.2rem;
padding:1px 0.3rem 0 0.3rem;
display:flex;
align-items:center;
}
.mtt-tab a:active { outline:0; text-decoration:none; }
.mtt-tab a>span {
.mtt-tab a > .title {
display:inline-block;
text-align:center;
min-width:75px;
max-width:195px;
cursor:pointer;
padding:0;
overflow:hidden;
text-overflow: ellipsis;
margin-left:0.3rem;
margin-right:0.3rem;
flex-grow:1;
}
.mtt-tab .list-action { display:none; }
.mtt-tab .mtt-img-button:hover,
@ -331,6 +336,19 @@ select.form-input {
background-color: var(--color-placeholder);
border-color: var(--color-placeholder-border);
}
.mtt-tab .counter {
display: inline-block;
min-width: 1.2rem;
height: 1rem;
border-radius: 1rem;
font-size: 0.8rem;
text-align: center;
background-color: #686868; /* #de5141 */
color: white;
}
.mtt-tab .counter.hidden {
display: none;
}
#tabs_buttons {
padding-left:2px; padding-right:2px;

View file

@ -28,6 +28,7 @@ class ListsController extends ApiController {
$t['list'][] = $this->prepareAllTasksList(); // show alltasks lists only for authorized user
$t['total'] = 1;
}
$t['time'] = time();
$q = $db->dq("SELECT * FROM {$db->prefix}lists $sqlWhere ORDER BY ow ASC, id ASC");
while ($r = $q->fetchAssoc())
{

View file

@ -98,6 +98,7 @@ class TasksController extends ApiController {
$t = array();
$t['total'] = 0;
$t['list'] = array();
$t['time'] = time();
$groupConcat = '';
if ($db::DBTYPE == DBConnection::DBTYPE_POSTGRES) {
@ -106,6 +107,7 @@ class TasksController extends ApiController {
else {
$groupConcat = "GROUP_CONCAT(tags.id) AS tags_ids, GROUP_CONCAT(tags.name) AS tags";
}
$q = $db->dq("
SELECT todo.*, todo.duedate IS NULL AS ddn, $groupConcat
FROM {$db->prefix}todolist AS todo
@ -246,6 +248,65 @@ class TasksController extends ApiController {
$this->response->data = $t;
}
function postNewCounter()
{
checkReadAccess();
$lists = $this->req->jsonBody['lists'] ?? false;
if (!is_array($lists)) {
$this->response->data = [ 'total' => 0 ];
return;
}
$userLists = [];
if (!haveWriteAccess()) {
$userLists = $this->getUserListsSimple(true);
if ($userLists) {
$sqlWhereList = "AND list_id IN (". implode(',', $userLists). ")";
// remove lists without access granted
$lists = array_filter($lists, function($item) use ($userLists) {
return in_array( (string)($item['listId'] ?? ''), $userLists );
});
}
}
$sqlWhereList = [];
foreach ($lists as $item) {
if (!isset($item['later'])) continue;
$later = (int)$item['later'];
if ($later <= 0) continue;
$sqlWhereList[] = "(list_id = ". (int)$item['listId']. " AND d_created > $later)";
}
$sqlWhere = implode(' OR ', $sqlWhereList);
$db = DBConnection::instance();
$a = [];
$q = $db->dq("SELECT list_id, COUNT(id) c FROM {$db->prefix}todolist
WHERE $sqlWhere GROUP BY list_id");
while ($r = $q->fetchAssoc()) {
$a[] = [
'listId' => (int)$r['list_id'],
'counter' => (int)$r['c'],
];
}
$b = [];
$list = (int) ($this->req->jsonBody['list'] ?? 0);
$later = (int) ($this->req->jsonBody['later'] ?? 0);
if ($list > 0 && $later > 0 && (!$userLists || in_array((string)$list, $userLists))) {
$q = $db->dq("SELECT id FROM {$db->prefix}todolist
WHERE list_id = $list AND d_created > $later");
while ($r = $q->fetchAssoc()) {
$b[] = (int)$r['id'];
}
}
$this->response->data = [
'total' => count($b) + count($a),
'tasks' => $b,
'lists' => $a
];
}
/* Private Functions */
private function newTaskInList(int $listId): ?array
@ -488,13 +549,17 @@ class TasksController extends ApiController {
return $t;
}
private function getUserListsSimple(): array
private function getUserListsSimple(bool $readOnly = false): array
{
$db = DBConnection::instance();
$sqlWhere = '';
if ($readOnly) {
$sqlWhere = "WHERE published=1";
}
$a = array();
$q = $db->dq("SELECT id,name FROM {$db->prefix}lists ORDER BY id ASC");
$q = $db->dq("SELECT id,name FROM {$db->prefix}lists $sqlWhere ORDER BY id ASC");
while($r = $q->fetchRow()) {
$a[$r[0]] = $r[1];
$a[ (string)$r[0] ] = (string)$r[1];
}
return $a;
}