From 585e514b68d1b8c00475709a44817bcb895bfaed Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sat, 16 Mar 2024 16:33:00 -0400 Subject: [PATCH] Improved tag selection on Review tab (#179) --- apprise_api/api/templates/config.html | 232 ++++++++++++++------- apprise_api/static/css/base.css | 9 +- apprise_api/static/css/theme-dark.min.css | 5 + apprise_api/static/css/theme-light.min.css | 18 +- 4 files changed, 184 insertions(+), 80 deletions(-) diff --git a/apprise_api/api/templates/config.html b/apprise_api/api/templates/config.html index 7b56991..b95b0c8 100644 --- a/apprise_api/api/templates/config.html +++ b/apprise_api/api/templates/config.html @@ -8,9 +8,9 @@

{% trans "Management for Config ID:" %} {{ key }}
  • info {% trans "Overview" %}
  • -
  • {% if not CONFIG_LOCK %}settings{% else %}lock{% endif %} {%trans "Configuration" %} +
  • {% if not CONFIG_LOCK %}settings{% else %}lock{% endif %} {%trans "Configuration" %}
  • -
  • {% if not CONFIG_LOCK %}web{% else %}lock{% endif %} {%trans "Review" %} +
  • {% if not CONFIG_LOCK %}web{% else %}lock{% endif %} {%trans "Review" %}
  • announcement {%trans "Notifications" %}
  • @@ -47,6 +47,11 @@
    {% trans "Getting Started" %}
    +
  • + {% blocktrans %} + Use the web Review section to review what was parsed/detected from your defined configuration. + {% endblocktrans %} +
  • {% blocktrans %} Use the announcement Notifications section to test out your saved configuration. @@ -54,15 +59,6 @@
    {% trans "Getting Started" %}
  • -
    -
    -

    - info - {% blocktrans %}Once you have successfully load at least one Apprise URL on the settings Configuration section, your loaded entries will display here. - {% endblocktrans %} -

    -
    -
    {% else %}
    {% trans "Apprise Configuration is Locked" %}
    @@ -147,11 +143,18 @@
    {% trans "Your Configuration Is Locked" %}
    {% if not CONFIG_LOCK %} -

    - {% blocktrans %}The following URLs have been detected:{% endblocktrans %} -

    -
    +
    +
    +

    + info + {% blocktrans %}Once you have successfully loaded at least one Apprise URL on the settings Configuration section, your loaded entries will display here. + {% endblocktrans %} +

    +
    +
    +
    {% trans "Loaded Configuration" %}
    +

    {% blocktrans %}Tip: Click on a tag (🏷️) to pre-select in the announcementNotifications section with the tag already pre-selected.{% endblocktrans %}

    @@ -189,24 +192,35 @@

    {% trans "Persistent Store Endpoints" %}

    {% if STATEFUL_MODE != 'disabled' %} async function main_init(){ + const params = new Proxy(new URLSearchParams(window.location.search), { + get: (searchParams, prop) => searchParams.get(prop), + }); + + if (params.title) { + document.querySelector('#id_title').value = params.title; + } + if (params.body) { + document.querySelector('#id_body').value = params.body; + } + +{% if not CONFIG_LOCK %} // disable the notification tab until we know for certain // a notification is possible document.querySelector('.config-overview li a[href="#notify"]') - .parentNode.classList.add('disabled'); + .parentNode.classList.add('disabled'); // Disable any has-config entries - document.querySelector('.has-config') - .style.display = 'none'; - + document.querySelectorAll('.has-config').forEach(function(e){ + e.style.display = 'none' + }); // Ensure we show our progress loader and reset our url list document.querySelector('#url-list').textContent = '' document.querySelector('#url-list-progress').style.display = null; -{% if not CONFIG_LOCK %} // Ensure no-config sections are visible - document.querySelector('.no-config') - .style.display = null; -{% endif %} + document.querySelectorAll('.no-config').forEach(function(e){ + e.style.display = null; + }); // perform a tag retrieval; start with 'all' let tags = ['all']; @@ -217,7 +231,7 @@

    {% trans "Persistent Store Endpoints" %}

    if(jsonResponse.status == 204) { // Take an early exit document.querySelector('#url-list-progress').style.display = 'none'; - document.querySelector('#url-list').textContent = '{% trans "There are no URLs defined. Click on the ⚙️ Configuration tab and define some." %}' + document.querySelector('#url-list').textContent = '' return; } else if(jsonResponse.status != 200) { @@ -234,42 +248,71 @@

    {% trans "Persistent Store Endpoints" %}

    const external_data = tags.concat(data.tags).reduce(function(result, item) { result[item] = null; return result; - }, {}) +}, {}) const chipElement = document.querySelector('.chips'); M.Chips.init(chipElement, { - placeholder: 'Optional Tag', - secondaryPlaceholder: 'Another Tag', + placeholder: '{% trans "Optional Tag" %}', + secondaryPlaceholder: '{% trans "Another Tag" %}', autocompleteOptions: { data: external_data, minLength: 0 }, - onChipAdd: function(e, chip) { + onChipAdd: function(e, data) { var $this = this; + const chip = data.childNodes[0].textContent; + document.querySelectorAll(`#url-list .chip[name=${chip}]`).forEach(function(e){ + if (!e.classList.contains('selected')) { + e.classList.add('selected'); + } + }) + document.querySelectorAll(`#url-list .chip.chip-notag`).forEach(function(e){ + if (e.classList.contains('selected')) { + e.classList.remove('selected'); + } + }) $this.chipsData.forEach(function(e, index) { if(!(e.tag in external_data)) $this.deleteChip(index); }) + }, + onChipDelete: function(e, data) { + var $this = this; + const chip = data.childNodes[0].textContent; + document.querySelectorAll(`#url-list .chip[name=${chip}]`).forEach(function(e){ + if (e.classList.contains('selected')) { + e.classList.remove('selected'); + } + }) + if ($this.chipsData.length == 0){ + // last item + document.querySelectorAll(`#url-list .chip.chip-notag`).forEach(function(e){ + if (!e.classList.contains('selected')) { + e.classList.add('selected'); + } + }) + } } }); - - // our GET parameters to be treated as template values - var tagRe = new RegExp('[^[A-Za-z0-9_-]+'); - const params = new Proxy(new URLSearchParams(window.location.search), { - get: (searchParams, prop) => searchParams.get(prop), +{% else %} + {# Empty external data set #} + const data = { + urls: [] + }; + M.Chips.init(chipElement, { + placeholder: '{% trans "Optional Tag" %}', + secondaryPlaceholder: '{% trans "Another Tag" %}' }); +{% endif %} + const chipInstance = M.Chips.getInstance(chipElement); if (params.tag) { + // our GET parameters to be treated as template values + var tagRe = new RegExp('[^[A-Za-z0-9_-]+'); params.tag.split(tagRe).forEach(function (tag, index) { - M.Chips.getInstance(chipElement).addChip({tag: tag, image: ''}); + chipInstance.addChip({tag: tag, image: ''}); }); } - if (params.title) { - document.querySelector('#id_title').value = params.title; - } - if (params.body) { - document.querySelector('#id_body').value = params.body; - } - +{% if not CONFIG_LOCK %} // Now build our our loaded list of configuration for our welcome page let urlList = document.createElement('ul'); @@ -281,51 +324,86 @@

    {% trans "Persistent Store Endpoints" %}

    li.setAttribute('class', 'card-panel'); li.appendChild(code); + urlList.appendChild(li); + // Store `all` tag + entry.tags.unshift('all') + + if (entry.tags.length === 1){ + // This entry triggers when no tags are defined + // Store '' (empty) tag for notice generation + entry.tags.unshift('') + + } // Get our tags associate with the URL entry.tags.forEach(function (tag) { let chip = document.createElement('div'); - chip.setAttribute('class', 'chip'); - chip.setAttribute('name', tag); - chip.setAttribute('href', `?tag=${tag}#notify`); - chip.textContent = `🏷️ ${tag}`; - li.appendChild(chip); - - chip.addEventListener('click', function(e) { - e.preventDefault(); - window.history.pushState(null, null, this.getAttribute('href')); - document.querySelector('.config-overview li a[href="#notify"]').click() - - const chipElement = document.querySelector('.chips'); - M.Chips.getInstance(chipElement).addChip({tag: this.getAttribute('name'), image: ''}); - }, false); + chip.setAttribute('class', `chip`); + if(tag.length > 0) { + // we're dealing with a valid tag + chip.setAttribute('name', tag); + chip.textContent = `🏷️ ${tag}`; + + const index = chipInstance.chipsData.findIndex(function (e) { + return e.tag === tag;}) + if (index >= 0) { + chip.classList.add('selected'); + } + li.appendChild(chip); + + chip.addEventListener('click', function(e) { + e.preventDefault(); + const index = chipInstance.chipsData.findIndex(function (e) { + return e.tag === tag; + }); + if(index < 0) { + chipInstance.addChip({tag: this.getAttribute('name'), image: ''}); + } else { + chipInstance.deleteChip(index); + } + }, false); + } else { + // no tags were defined for this element + chip.classList.add('chip-notag'); + chip.textContent = '🔖 ' + 'no-tag'; + if (chipInstance.chipsData.length === 0) { + chip.classList.add('selected'); + } + li.appendChild(chip); + chip.addEventListener('click', function(e) { + e.preventDefault(); + while(chipInstance.chipsData.length > 0){ + chipInstance.deleteChip(0); + } + chip.classList.add('selected'); + }, false); + } }); - - urlList.appendChild(li); }); +{% endif %} +{% if not CONFIG_LOCK %} // Store our new list document.querySelector('#url-list-progress').style.display = 'none'; document.querySelector('#url-list').textContent = '' if(urlList.childNodes.length > 0) { // Ensure has-config sections are visible - document.querySelector('.has-config') - .style.display = null; + document.querySelectorAll('.has-config').forEach(function(e){ + e.style.display = null; + }); // Remove our restrictions on sending notifications document.querySelector('.config-overview li a[href="#notify"]') .parentNode.classList.remove('disabled'); - {% if not CONFIG_LOCK %} // Disable any no-config entries - document.querySelector('.no-config') - .style.display = 'none'; - {% endif %} + document.querySelectorAll('.no-config').forEach(function(e){ + e.style.display = 'none'; + }); // Save our list to the screen document.querySelector('#url-list').appendChild(urlList); - {% if not CONFIG_LOCK %} // // Load our configuration now into the configuration tab // @@ -362,14 +440,15 @@

    {% trans "Persistent Store Endpoints" %}

    } document.querySelector('#id_format').dispatchEvent(event); } - {% endif %} } else { document.querySelector('#url-list').textContent = '{% trans "There are no Apprise URL(s) loaded." %}' } + {% endif %} return null; } +{% if not CONFIG_LOCK %} function config_init() { // over-ride manual submit for a nicer user experience document.querySelector('#addconfig').onsubmit = function(event) { @@ -443,6 +522,7 @@

    {% trans "Persistent Store Endpoints" %}

    return false; } } +{% endif %} function form_file_input_hack() { /* @@ -478,7 +558,8 @@

    {% trans "Persistent Store Endpoints" %}

    selected.textContent = file.name; }); }); - } +} + function notify_init() { // over-ride manual submit for a nicer user experience document.querySelector('#donotify').onsubmit = function(event) { @@ -571,16 +652,6 @@

    {% trans "Persistent Store Endpoints" %}

    return false; } } - -/* Initialize our page */ -main_init(); -{% if not CONFIG_LOCK %} -/* Initialze our configuration */ -config_init(); - -{% endif %} -notify_init(); -form_file_input_hack(); {% endif %} {% endblock %} @@ -599,5 +670,14 @@

    {% trans "Persistent Store Endpoints" %}

    // Hide tag field since we use the pretty Materialize Chip setup instead document.querySelector('#id_tag').style.display = 'none'; +/* Initialize our page */ +main_init(); + +{% if not CONFIG_LOCK %} +/* Initialze our configuration */ +config_init(); +{% endif %} +notify_init(); +form_file_input_hack(); {% endif %} {% endblock %} diff --git a/apprise_api/static/css/base.css b/apprise_api/static/css/base.css index ce69914..999a180 100644 --- a/apprise_api/static/css/base.css +++ b/apprise_api/static/css/base.css @@ -125,10 +125,11 @@ textarea { float: left; } -#url-list .chip { +.chip { margin: 0.3rem; background-color: inherit; - border: 1px solid #e4e4e4; + border: 1px solid #464646; + color: #464646; cursor: pointer; } @@ -232,3 +233,7 @@ code.config-id { padding: 0 1em; overflow: hidden; } + +.chip.selected { + font-weight: 600; +} diff --git a/apprise_api/static/css/theme-dark.min.css b/apprise_api/static/css/theme-dark.min.css index cbed69f..4f125af 100644 --- a/apprise_api/static/css/theme-dark.min.css +++ b/apprise_api/static/css/theme-dark.min.css @@ -1324,3 +1324,8 @@ input[type=range]::-ms-thumb { color: #ebcb8b; background-color: #2e3440; } + +.chip.selected { + color: #fff; + background-color: #258528!important; +} diff --git a/apprise_api/static/css/theme-light.min.css b/apprise_api/static/css/theme-light.min.css index e98a73c..77c6353 100644 --- a/apprise_api/static/css/theme-light.min.css +++ b/apprise_api/static/css/theme-light.min.css @@ -1,7 +1,21 @@ .tabs .tab a {background-color: #f3f3f3;} -.tabs.tabs-transparent .tab a,.tabs.tabs-transparent .tab.disabled a,.tabs.tabs-transparent .tab.disabled a:hover, .tab.disabled i.material-icons{color:#a7a7a7} -.tabs .tab.disabled a,.tabs .tab.disabled a:hover{background-color: #f3f3f3; color:#a7a7a7} +.tabs.tabs-transparent .tab a, +.tabs.tabs-transparent .tab.disabled a, +.tabs.tabs-transparent .tab.disabled a:hover, +.tab.disabled i.material-icons{ + color:#a7a7a7 +} +.tabs .tab.disabled a, +.tabs .tab.disabled a:hover { + background-color: #f3f3f3; + color:#a7a7a7 +} .file-selected { color: #039be5; background-color: #f3f3f3; } + +.chip.selected { + color: #fff; + background-color: #258528!important; +}