Reworked web UI editing

This commit is contained in:
krateng 2023-10-17 19:36:59 +02:00
parent 25ba050d30
commit d8420cdb67
21 changed files with 318 additions and 230 deletions

View File

@ -23,6 +23,7 @@
<script src="/neopolitan.js"></script> <script src="/neopolitan.js"></script>
<script src="/upload.js"></script> <script src="/upload.js"></script>
<script src="/notifications.js"></script> <script src="/notifications.js"></script>
<script src="/edit.js"></script>
<script> <script>
const defaultpicks = { const defaultpicks = {
topartists: '{{ settings["DEFAULT_RANGE_STARTPAGE"] }}', topartists: '{{ settings["DEFAULT_RANGE_STARTPAGE"] }}',

View File

@ -5,7 +5,6 @@
{% block scripts %} {% block scripts %}
<script src="/statselect.js"></script> <script src="/statselect.js"></script>
<script src="/edit.js"></script>
{% endblock %} {% endblock %}
{% set album = filterkeys.album %} {% set album = filterkeys.album %}
@ -20,13 +19,21 @@
{% block icon_bar %} {% block icon_bar %}
{% if adminmode %} {% if adminmode %}
{% include 'icons/edit.jinja' %} {% include 'icons/edit.jinja' %}
<div class="iconsubset mergeicons" data-entity_type="album" data-entity_id="{{ info.id }}" data-entity_name="{{ info.album.albumtitle }}">
{% include 'icons/merge.jinja' %} {% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %} {% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_unmark.jinja' %}
{% include 'icons/merge_cancel.jinja' %} {% include 'icons/merge_cancel.jinja' %}
</div>
<div class="iconsubset associateicons" data-entity_type="album" data-entity_id="{{ info.id }}" data-entity_name="{{ info.album.albumtitle }}">
{% include 'icons/add_album.jinja' %} {% include 'icons/add_album.jinja' %}
{% include 'icons/association_mark.jinja' %} {% include 'icons/association_mark.jinja' %}
{% include 'icons/association_unmark.jinja' %}
{% include 'icons/association_cancel.jinja' %} {% include 'icons/association_cancel.jinja' %}
<script>showValidMergeIcons();</script> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -6,7 +6,6 @@
{% block scripts %} {% block scripts %}
<script src="/statselect.js"></script> <script src="/statselect.js"></script>
<script src="/edit.js"></script>
{% endblock %} {% endblock %}
{% set artist = filterkeys.artist %} {% set artist = filterkeys.artist %}
@ -30,11 +29,19 @@
{% block icon_bar %} {% block icon_bar %}
{% if adminmode %} {% if adminmode %}
{% include 'icons/edit.jinja' %} {% include 'icons/edit.jinja' %}
<div class="iconsubset mergeicons" data-entity_type="artist" data-entity_id="{{ info.id }}" data-entity_name="{{ info.artist }}">
{% include 'icons/merge.jinja' %} {% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %} {% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_unmark.jinja' %}
{% include 'icons/merge_cancel.jinja' %} {% include 'icons/merge_cancel.jinja' %}
</div>
<div class="iconsubset associateicons" data-entity_type="artist" data-entity_id="{{ info.id }}" data-entity_name="{{ info.artist }}">
{% include 'icons/add_artist.jinja' %} {% include 'icons/add_artist.jinja' %}
<script>showValidMergeIcons();</script> {% include 'icons/association_cancel.jinja' %}
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,4 +1,4 @@
<div title="Add to Album" id="associatealbumicon" class="clickable_icon" onclick="associate()"> <div title="Add to Album" id="associatealbumicon" class="clickable_icon" onclick="associate(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path d="M2 4.75C2 3.784 2.784 3 3.75 3h4.971a1.75 1.75 0 0 1 1.447.765l1.404 2.063a.25.25 0 0 0 .207.11h8.471c.966 0 1.75.783 1.75 1.75V19.25A1.75 1.75 0 0 1 20.25 21H4.75a.75.75 0 0 1 0-1.5h15.5a.25.25 0 0 0 .25-.25V7.688a.25.25 0 0 0-.25-.25h-8.471a1.751 1.751 0 0 1-1.447-.766L8.928 4.609a.252.252 0 0 0-.207-.109H3.75a.25.25 0 0 0-.25.25v3.5a.75.75 0 0 1-1.5 0v-3.5Z"></path> <path d="M2 4.75C2 3.784 2.784 3 3.75 3h4.971a1.75 1.75 0 0 1 1.447.765l1.404 2.063a.25.25 0 0 0 .207.11h8.471c.966 0 1.75.783 1.75 1.75V19.25A1.75 1.75 0 0 1 20.25 21H4.75a.75.75 0 0 1 0-1.5h15.5a.25.25 0 0 0 .25-.25V7.688a.25.25 0 0 0-.25-.25h-8.471a1.751 1.751 0 0 1-1.447-.766L8.928 4.609a.252.252 0 0 0-.207-.109H3.75a.25.25 0 0 0-.25.25v3.5a.75.75 0 0 1-1.5 0v-3.5Z"></path>
<path d="m9.308 12.5-2.104-2.236a.75.75 0 1 1 1.092-1.028l3.294 3.5a.75.75 0 0 1 0 1.028l-3.294 3.5a.75.75 0 1 1-1.092-1.028L9.308 14H4.09a2.59 2.59 0 0 0-2.59 2.59v3.16a.75.75 0 0 1-1.5 0v-3.16a4.09 4.09 0 0 1 4.09-4.09h5.218Z"></path> <path d="m9.308 12.5-2.104-2.236a.75.75 0 1 1 1.092-1.028l3.294 3.5a.75.75 0 0 1 0 1.028l-3.294 3.5a.75.75 0 1 1-1.092-1.028L9.308 14H4.09a2.59 2.59 0 0 0-2.59 2.59v3.16a.75.75 0 0 1-1.5 0v-3.16a4.09 4.09 0 0 1 4.09-4.09h5.218Z"></path>

View File

@ -1,4 +1,4 @@
<div title="Add Artist" id="associateartisticon" class="clickable_icon" onclick="associate()"> <div title="Add Artist" id="associateartisticon" class="clickable_icon" onclick="associate(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path d="M4 9.5a5 5 0 1 1 7.916 4.062 7.973 7.973 0 0 1 5.018 7.166.75.75 0 1 1-1.499.044 6.469 6.469 0 0 0-12.932 0 .75.75 0 0 1-1.499-.044 7.972 7.972 0 0 1 5.059-7.181A4.994 4.994 0 0 1 4 9.5ZM9 6a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Zm10.25-5a.75.75 0 0 1 .75.75V4h2.25a.75.75 0 0 1 0 1.5H20v2.25a.75.75 0 0 1-1.5 0V5.5h-2.25a.75.75 0 0 1 0-1.5h2.25V1.75a.75.75 0 0 1 .75-.75Z"></path> <path d="M4 9.5a5 5 0 1 1 7.916 4.062 7.973 7.973 0 0 1 5.018 7.166.75.75 0 1 1-1.499.044 6.469 6.469 0 0 0-12.932 0 .75.75 0 0 1-1.499-.044 7.972 7.972 0 0 1 5.059-7.181A4.994 4.994 0 0 1 4 9.5ZM9 6a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7Zm10.25-5a.75.75 0 0 1 .75.75V4h2.25a.75.75 0 0 1 0 1.5H20v2.25a.75.75 0 0 1-1.5 0V5.5h-2.25a.75.75 0 0 1 0-1.5h2.25V1.75a.75.75 0 0 1 .75-.75Z"></path>
</svg> </svg>

View File

@ -1,7 +1,6 @@
<div title="Cancel Track Association" id="associationcancelicon" class="clickable_icon" onclick="umarkForAssociate(this)"> <div title="Cancel associating" class="associatecancelicon clickable_icon" onclick="cancelAssociate(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M2.345 20.595 8.47 14.47q.219-.22.53-.22.311 0 .53.22.22.219.22.53 0 .311-.22.53l-6.125 6.125q-.219.22-.53.22-.311 0-.53-.22-.22-.219-.22-.53 0-.311.22-.53Z"></path> <path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM5.834 19.227A9.464 9.464 0 0 0 12 21.5a9.5 9.5 0 0 0 9.5-9.5 9.464 9.464 0 0 0-2.273-6.166ZM2.5 12a9.464 9.464 0 0 0 2.273 6.166L18.166 4.773A9.463 9.463 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Z">
<path d="m16.72 11.97.358-.358a6.738 6.738 0 0 1 2.326-1.518l1.896-.738a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.521 1.34a8.663 8.663 0 0 1-2.243 3.265.75.75 0 0 1-1.01-1.11 7.132 7.132 0 0 0 1.854-2.699l.521-1.34a1.75 1.75 0 0 1 2.869-.603l6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.896.737a5.26 5.26 0 0 0-1.81 1.18l-.358.358a.749.749 0 1 1-1.06-1.06Zm-12.549-.738a1.75 1.75 0 0 1 .757-2.92l3.366-.962.412 1.443-3.366.961a.25.25 0 0 0-.108.417l8.597 8.597a.25.25 0 0 0 .417-.108l.961-3.366 1.443.412-.962 3.366a1.75 1.75 0 0 1-2.92.757Z"></path> </path>
<path d="m3.405 2.095 18.75 18.75q.22.219.22.53 0 .311-.22.53-.219.22-.53.22-.311 0-.53-.22L2.345 3.155q-.22-.219-.22-.53 0-.311.22-.53.219-.22.53-.22.311 0 .53.22Z"></path>
</svg> </svg>
</div> </div>

View File

@ -1,4 +1,4 @@
<div title="Mark to associate artists or album" id="associationmarkicon" class="clickable_icon" onclick="markForAssociate(this)"> <div title="Mark for association" class="associationmarkicon clickable_icon" onclick="markForAssociate(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="m16.114 1.553 6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.63.633a5.67 5.67 0 0 0-3.395 3.725l-1.131 3.959a1.75 1.75 0 0 1-2.92.757L9 16.061l-5.595 5.594a.749.749 0 1 1-1.06-1.06L7.939 15l-3.768-3.768a1.75 1.75 0 0 1 .757-2.92l3.959-1.131a5.666 5.666 0 0 0 3.725-3.395l.633-1.63a1.75 1.75 0 0 1 2.869-.603ZM5.232 10.171l8.597 8.597a.25.25 0 0 0 .417-.108l1.131-3.959A7.17 7.17 0 0 1 19.67 9.99l1.63-.634a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.634 1.63a7.17 7.17 0 0 1-4.711 4.293L5.34 9.754a.25.25 0 0 0-.108.417Z"></path> <path d="m16.114 1.553 6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.63.633a5.67 5.67 0 0 0-3.395 3.725l-1.131 3.959a1.75 1.75 0 0 1-2.92.757L9 16.061l-5.595 5.594a.749.749 0 1 1-1.06-1.06L7.939 15l-3.768-3.768a1.75 1.75 0 0 1 .757-2.92l3.959-1.131a5.666 5.666 0 0 0 3.725-3.395l.633-1.63a1.75 1.75 0 0 1 2.869-.603ZM5.232 10.171l8.597 8.597a.25.25 0 0 0 .417-.108l1.131-3.959A7.17 7.17 0 0 1 19.67 9.99l1.63-.634a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.634 1.63a7.17 7.17 0 0 1-4.711 4.293L5.34 9.754a.25.25 0 0 0-.108.417Z"></path>
</svg> </svg>

View File

@ -0,0 +1,7 @@
<div title="Unmark for Association"class="associationunmarkicon clickable_icon" onclick="umarkForAssociate(this)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M2.345 20.595 8.47 14.47q.219-.22.53-.22.311 0 .53.22.22.219.22.53 0 .311-.22.53l-6.125 6.125q-.219.22-.53.22-.311 0-.53-.22-.22-.219-.22-.53 0-.311.22-.53Z"></path>
<path d="m16.72 11.97.358-.358a6.738 6.738 0 0 1 2.326-1.518l1.896-.738a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.521 1.34a8.663 8.663 0 0 1-2.243 3.265.75.75 0 0 1-1.01-1.11 7.132 7.132 0 0 0 1.854-2.699l.521-1.34a1.75 1.75 0 0 1 2.869-.603l6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.896.737a5.26 5.26 0 0 0-1.81 1.18l-.358.358a.749.749 0 1 1-1.06-1.06Zm-12.549-.738a1.75 1.75 0 0 1 .757-2.92l3.366-.962.412 1.443-3.366.961a.25.25 0 0 0-.108.417l8.597 8.597a.25.25 0 0 0 .417-.108l.961-3.366 1.443.412-.962 3.366a1.75 1.75 0 0 1-2.92.757Z"></path>
<path d="m3.405 2.095 18.75 18.75q.22.219.22.53 0 .311-.22.53-.219.22-.53.22-.311 0-.53-.22L2.345 3.155q-.22-.219-.22-.53 0-.311.22-.53.219-.22.53-.22.311 0 .53.22Z"></path>
</svg>
</div>

View File

@ -1,4 +1,4 @@
<div title="Merge" id="mergeicon" class="clickable_icon hide" onclick="merge()"> <div title="Merge" id="mergeicon" class="clickable_icon" onclick="merge(this)">
<svg viewBox="0 0 16 16" width="24" height="24"> <svg viewBox="0 0 16 16" width="24" height="24">
<path fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path> <path fill-rule="evenodd" d="M5 3.254V3.25v.005a.75.75 0 110-.005v.004zm.45 1.9a2.25 2.25 0 10-1.95.218v5.256a2.25 2.25 0 101.5 0V7.123A5.735 5.735 0 009.25 9h1.378a2.251 2.251 0 100-1.5H9.25a4.25 4.25 0 01-3.8-2.346zM12.75 9a.75.75 0 100-1.5.75.75 0 000 1.5zm-8.5 4.5a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
</svg> </svg>

View File

@ -1,5 +1,6 @@
<div title="Cancel merge" id="mergecancelicon" class="clickable_icon hide" onclick="cancelMerge()"> <div title="Cancel merging" id="mergecancelicon" class="clickable_icon" onclick="cancelMerge(this)">
<svg viewBox="0 0 16 16" width="24" height="24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M10.72 1.227a.75.75 0 011.06 0l.97.97.97-.97a.75.75 0 111.06 1.061l-.97.97.97.97a.75.75 0 01-1.06 1.06l-.97-.97-.97.97a.75.75 0 11-1.06-1.06l.97-.97-.97-.97a.75.75 0 010-1.06zM12.75 6.5a.75.75 0 00-.75.75v3.378a2.251 2.251 0 101.5 0V7.25a.75.75 0 00-.75-.75zm0 5.5a.75.75 0 100 1.5.75.75 0 000-1.5zM2.5 3.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.25 1a2.25 2.25 0 00-.75 4.372v5.256a2.251 2.251 0 101.5 0V5.372A2.25 2.25 0 003.25 1zm0 11a.75.75 0 100 1.5.75.75 0 000-1.5z"></path> <path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM5.834 19.227A9.464 9.464 0 0 0 12 21.5a9.5 9.5 0 0 0 9.5-9.5 9.464 9.464 0 0 0-2.273-6.166ZM2.5 12a9.464 9.464 0 0 0 2.273 6.166L18.166 4.773A9.463 9.463 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Z">
</path>
</svg> </svg>
</div> </div>

View File

@ -1,4 +1,4 @@
<div title="Mark for merging" id="mergemarkicon" class="clickable_icon hide" onclick="markForMerge()"> <div title="Mark for merging" id="mergemarkicon" class="clickable_icon" onclick="markForMerge(this)">
<svg viewBox="0 0 16 16" width="24" height="24"> <svg viewBox="0 0 16 16" width="24" height="24">
<path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path> <path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path>
</svg> </svg>

View File

@ -0,0 +1,5 @@
<div title="Unmark from merge" id="mergeunmarkicon" class="clickable_icon" onclick="unmarkForMerge(this)">
<svg viewBox="0 0 16 16" width="24" height="24">
<path fill-rule="evenodd" d="M10.72 1.227a.75.75 0 011.06 0l.97.97.97-.97a.75.75 0 111.06 1.061l-.97.97.97.97a.75.75 0 01-1.06 1.06l-.97-.97-.97.97a.75.75 0 11-1.06-1.06l.97-.97-.97-.97a.75.75 0 010-1.06zM12.75 6.5a.75.75 0 00-.75.75v3.378a2.251 2.251 0 101.5 0V7.25a.75.75 0 00-.75-.75zm0 5.5a.75.75 0 100 1.5.75.75 0 000-1.5zM2.5 3.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.25 1a2.25 2.25 0 00-.75 4.372v5.256a2.251 2.251 0 101.5 0V5.372A2.25 2.25 0 003.25 1zm0 11a.75.75 0 100 1.5.75.75 0 000-1.5z"></path>
</svg>
</div>

View File

@ -28,11 +28,12 @@
{% set firstindex = amountkeys.page * amountkeys.perpage %} {% set firstindex = amountkeys.page * amountkeys.perpage %}
{% set lastindex = firstindex + amountkeys.perpage %} {% set lastindex = firstindex + amountkeys.perpage %}
{% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %} {% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %}
<table class='list'> <table class='list'>
{% for e in charts %} {% for e in charts %}
{% if loop.index0 >= firstindex and loop.index0 < lastindex %} {% if loop.index0 >= firstindex and loop.index0 < lastindex %}
<tr> <tr class="listrow associateicons" data-entity_id="{{ e['album_id'] }}" data-entity_type="album" data-entity_name="{{ e['album'].albumtitle }}">
<!-- Rank --> <!-- Rank -->
<td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td> <td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td>
<!-- Rank change --> <!-- Rank change -->
@ -45,7 +46,7 @@
{% endif %} {% endif %}
<!-- artist --> <!-- artist -->
{{ entityrow.row(e['album']) }} {{ entityrow.row(e['album'],adminmode=True) }}
<!-- scrobbles --> <!-- scrobbles -->
<td class="amount">{{ links.link_scrobbles([{'album':e.album,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td> <td class="amount">{{ links.link_scrobbles([{'album':e.album,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td>

View File

@ -30,12 +30,13 @@
{% set lastindex = firstindex + amountkeys.perpage %} {% set lastindex = firstindex + amountkeys.perpage %}
{% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %} {% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %}
<table class='list'> <table class='list'>
{% for e in charts %} {% for e in charts %}
{% if loop.index0 >= firstindex and loop.index0 < lastindex %} {% if loop.index0 >= firstindex and loop.index0 < lastindex %}
<tr> <tr class="listrow associateicons" data-entity_id="{{ e['artist_id'] }}" data-entity_type="artist" data-entity_name="{{ e['artist'] }}">
<!-- Rank --> <!-- Rank -->
<td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td> <td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td>
<!-- Rank change --> <!-- Rank change -->
@ -48,7 +49,7 @@
{% endif %} {% endif %}
<!-- artist --> <!-- artist -->
{{ entityrow.row(e['artist']) }} {{ entityrow.row(e['artist'],adminmode=True) }}
<!-- scrobbles --> <!-- scrobbles -->
<td class="amount">{{ links.link_scrobbles([{'artist':e['artist'],'associated':True,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td> <td class="amount">{{ links.link_scrobbles([{'artist':e['artist'],'associated':True,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td>

View File

@ -28,11 +28,12 @@
{% set firstindex = amountkeys.page * amountkeys.perpage %} {% set firstindex = amountkeys.page * amountkeys.perpage %}
{% set lastindex = firstindex + amountkeys.perpage %} {% set lastindex = firstindex + amountkeys.perpage %}
{% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %} {% set maxbar = charts[0]['scrobbles'] if charts != [] else 0 %}
<table class='list'> <table class='list'>
{% for e in charts %} {% for e in charts %}
{% if loop.index0 >= firstindex and loop.index0 < lastindex %} {% if loop.index0 >= firstindex and loop.index0 < lastindex %}
<tr> <tr class="listrow associateicons" data-entity_id="{{ e['track_id'] }}" data-entity_type="track" data-entity_name="{{ e['track'].title }}">
<!-- Rank --> <!-- Rank -->
<td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td> <td class="rank">{%if loop.changed(e.scrobbles) %}#{{ e.rank }}{% endif %}</td>
<!-- Rank change --> <!-- Rank change -->
@ -45,7 +46,7 @@
{% endif %} {% endif %}
<!-- artist --> <!-- artist -->
{{ entityrow.row(e['track']) }} {{ entityrow.row(e['track'],adminmode=True) }}
<!-- scrobbles --> <!-- scrobbles -->
<td class="amount">{{ links.link_scrobbles([{'track':e.track,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td> <td class="amount">{{ links.link_scrobbles([{'track':e.track,'timerange':limitkeys.timerange}],amount=e['scrobbles']) }}</td>

View File

@ -6,22 +6,14 @@
{% set firstindex = amountkeys.page * amountkeys.perpage %} {% set firstindex = amountkeys.page * amountkeys.perpage %}
{% set lastindex = firstindex + amountkeys.perpage %} {% set lastindex = firstindex + amountkeys.perpage %}
<script src="/edit.js"></script>
<table class='list'> <table class='list'>
{% for e in list %} {% for e in list %}
{% if loop.index0 >= firstindex and loop.index0 < lastindex %} {% if loop.index0 >= firstindex and loop.index0 < lastindex %}
<tr class="listrow unmarked" data-entity_id="{{ e['track_id'] }}" data-entity_type="track" data-entity_name="{{ e['track'].title }}"> <tr class="listrow associateicons" data-entity_id="{{ e['track_id'] }}" data-entity_type="track" data-entity_name="{{ e['track'].title }}">
<!-- artist --> <!-- artist -->
{{ entityrow.row(e['track']) }} {{ entityrow.row(e['track'],adminmode=adminmode) }}
{% with inlineicons = True %}
<td>
{% include 'icons/association_mark.jinja' %}
{% include 'icons/association_cancel.jinja' %}
</td>
{% endwith %}
</tr> </tr>
{% endif %} {% endif %}
@ -30,9 +22,6 @@
<script> <script>
var listrows = document.getElementsByClassName('listrow');
for (var row of listrows) {
toggleAssociationIcons(row);
}
</script> </script>

View File

@ -6,9 +6,6 @@
{% import 'snippets/entityrow.jinja' as entityrow %} {% import 'snippets/entityrow.jinja' as entityrow %}
<script src="/edit.js"></script>
<table class='list'> <table class='list'>
{% for s in scrobbles -%} {% for s in scrobbles -%}
{%- if loop.index0 >= firstindex and loop.index0 < lastindex -%} {%- if loop.index0 >= firstindex and loop.index0 < lastindex -%}

View File

@ -1,4 +1,4 @@
{% macro row(entity,counting=[]) %} {% macro row(entity,counting=[],adminmode=False) %}
{% import 'snippets/links.jinja' as links %} {% import 'snippets/links.jinja' as links %}
@ -35,4 +35,11 @@
{% endif %} {% endif %}
{% if adminmode and (entity is mapping) %}
<td>
{% include 'icons/association_mark.jinja' %}
{% include 'icons/association_unmark.jinja' %}
</td>
{% endif %}
{% endmacro %} {% endmacro %}

View File

@ -5,7 +5,6 @@
{% block scripts %} {% block scripts %}
<script src="/statselect.js"></script> <script src="/statselect.js"></script>
<script src="/edit.js"></script>
<script> <script>
function scrobble(encodedtrack) { function scrobble(encodedtrack) {
neo.xhttprequest('/apis/mlj_1/newscrobble?nofix&' + encodedtrack,data={},method="POST").then(response=>{window.location.reload()}); neo.xhttprequest('/apis/mlj_1/newscrobble?nofix&' + encodedtrack,data={},method="POST").then(response=>{window.location.reload()});
@ -25,12 +24,19 @@
{% block icon_bar %} {% block icon_bar %}
{% if adminmode %} {% if adminmode %}
{% include 'icons/edit.jinja' %} {% include 'icons/edit.jinja' %}
<div class="iconsubset mergeicons" data-entity_type="track" data-entity_id="{{ info.id }}" data-entity_name="{{ info.track.title }}">
{% include 'icons/merge.jinja' %} {% include 'icons/merge.jinja' %}
{% include 'icons/merge_mark.jinja' %} {% include 'icons/merge_mark.jinja' %}
{% include 'icons/merge_unmark.jinja' %}
{% include 'icons/merge_cancel.jinja' %} {% include 'icons/merge_cancel.jinja' %}
</div>
<div class="iconsubset associateicons" data-entity_type="track" data-entity_id="{{ info.id }}" data-entity_name="{{ info.track.title }}">
{% include 'icons/association_mark.jinja' %} {% include 'icons/association_mark.jinja' %}
{% include 'icons/association_unmark.jinja' %}
{% include 'icons/association_cancel.jinja' %} {% include 'icons/association_cancel.jinja' %}
<script>showValidMergeIcons();</script> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -67,6 +67,10 @@ div#icon_bar {
right:30px; right:30px;
top:30px; top:30px;
} }
.iconsubset {
display: inline-block;
padding-left:20px;
}
div#icon_bar div.clickable_icon { div#icon_bar div.clickable_icon {
display: inline-block; display: inline-block;
@ -94,14 +98,75 @@ div.clickable_icon.danger:hover svg {
display: inline-block; display: inline-block;
} }
.list tr.marked { .list {
background-color: rgba(50,20,0,0.5); --color_bg_merge: rgba(0,0,90,0.7);
--color_fg_merge: lightblue;
--color_bg_associate: rgba(50,20,0,0.7);
--color_fg_associate: orange;
} }
.list tr.marked #associationmarkicon {
display:none; .list tr.marked_for_associate {
background-color: var(--color_bg_associate);
color: var(--color_fg_associate);
} }
.list tr:not(.marked) #associationcancelicon { .list tr.marked_for_merge {
display:none; background-color: var(--color_bg_merge);
color: var(--color_fg_merge);;
}
@keyframes slideBackground {
0% {
background-position: 100% 0;
}
50% {
background-position: 0 0;
}
100% {
background-position: 100% 0;
}
}
@keyframes colorChange {
0% {
color: var(--color_fg_associate);
}
50% {
color: var(--color_fg_merge);
}
100% {
color: var(--color_fg_associate);
}
}
.list tr.marked_for_associate.marked_for_merge {
background: linear-gradient(to left, var(--color_bg_associate), var(--color_bg_merge));
background-size: 100% 100%;
animation: colorChange 4s infinite ease-in-out;
}
/* this is just to 'factor out' that big selector down there.
we want icons to not be displayed in list rows, but show them with reduced opacity in the top bar */
.list {
--display_inactive_icons: none;
}
#icon_bar {
--display_inactive_icons: inline-block;
}
.associateicons.marked_for_associate .associationmarkicon, /* already marked, cant mark again */
.associateicons:not(.marked_for_associate) .associationunmarkicon, /* not marked, cant unmark */
.associateicons:not(.somethingmarked_for_associate) .associatecancelicon, /* cant cancel when nothing is marked */
.mergeicons.marked_for_merge #mergemarkicon, /* already marked, cant mark again */
.mergeicons:not(.marked_for_merge) #mergeunmarkicon, /* not marked, cant unmark */
.mergeicons:not(.somethingmarked_for_merge) #mergecancelicon, /* cant cancel when nothing is marked */
.mergeicons:not(.somethingmarked_for_merge) #mergeicon, /* can't merge when nothing is selected */
.mergeicons.marked_for_merge #mergeicon, /* cant merge into one of the things we have selected */
.associateicons:not(.sources_marked_for_associate) #associatealbumicon,
.associateicons:not(.sources_marked_for_associate) #associateartisticon /* nothing marked yet, can't associate with this */
{
pointer-events: none;
opacity:0.5;
display: var(--display_inactive_icons);
} }
/** /**

View File

@ -187,186 +187,117 @@ function doneEditing() {
} }
} }
// MERGING // MERGING AND ASSOCIATION
function showValidMergeIcons() { const associate_targets = {
album: ['artist'],
track: ['album','artist'],
artist: []
};
// merge const associate_sources = {
artist: ['album','track'],
album: ['track'],
track: []
};
function getStoredList(key) {
const lcst = window.sessionStorage; const lcst = window.sessionStorage;
var key = "marked_for_merge_" + entity_type;
var current_stored = (lcst.getItem(key) || '').split(","); var current_stored = (lcst.getItem(key) || '').split(",");
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x)); current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
return current_stored;
var mergeicon = document.getElementById('mergeicon'); }
var mergemarkicon = document.getElementById('mergemarkicon'); function storeList(key,list) {
var mergecancelicon = document.getElementById('mergecancelicon'); const lcst = window.sessionStorage;
list = [...new Set(list)];
mergeicon.classList.add('hide'); lcst.setItem(key,list); //this already formats it correctly
mergemarkicon.classList.add('hide');
mergecancelicon.classList.add('hide');
if (current_stored.length == 0) {
mergemarkicon.classList.remove('hide');
}
else {
mergecancelicon.classList.remove('hide');
if (current_stored.includes(entity_id)) {
}
else {
mergemarkicon.classList.remove('hide');
mergeicon.classList.remove('hide');
}
}
// mark for association
if ((entity_type == 'track') || (entity_type == 'album')) {
const lcst = window.sessionStorage;
var key = "marked_for_associate_" + entity_type;
var current_stored = (lcst.getItem(key) || '').split(",");
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
var associationmarkicon = document.getElementById('associationmarkicon');
var associationcancelicon = document.getElementById('associationcancelicon');
associationmarkicon.classList.add('hide');
associationcancelicon.classList.add('hide');
if (current_stored.length == 0) {
associationmarkicon.classList.remove('hide');
}
else {
associationcancelicon.classList.remove('hide');
if (current_stored.includes(entity_id)) {
}
else {
associationmarkicon.classList.remove('hide');
}
}
if (entity_type == 'track') {
associationmarkicon.title = "Mark this track to add to album or add artist";
}
else {
associationmarkicon.title = "Mark this album to add artist";
}
}
// association confirm
if ((entity_type == 'artist') || (entity_type == 'album')) {
var target_entity_types = {artist:['album','track'], album:['track']};
var to_associate = {};
var to_associate_all = [];
for (var target_entity_type of target_entity_types[entity_type]) {
const lcst = window.sessionStorage;
var key = "marked_for_associate_" + target_entity_type;
var current_stored = (lcst.getItem(key) || '').split(",");
to_associate[target_entity_type] = current_stored.filter((x)=>x).map((x)=>parseInt(x));
to_associate_all = to_associate_all.concat(to_associate[target_entity_type]);
}
var associateicon = document.getElementById('associate' + entity_type + 'icon');
associateicon.classList.add('hide');
if (to_associate_all.length == 0) {
}
else {
associateicon.classList.remove('hide');
if (entity_type == 'artist') {
associateicon.title = "Add this artist to " + to_associate['album'].length + " albums and " + to_associate['track'].length + " tracks";
}
else {
associateicon.title = "Add " + to_associate['track'].length + " tracks to this album";
}
}
}
} }
function markForMerge() {
const lcst = window.sessionStorage; function markForMerge(element) {
var key = "marked_for_merge_" + entity_type; const parentElement = element.closest('[data-entity_id]');
var current_stored = (lcst.getItem(key) || '').split(",");
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x)); var entity_type = parentElement.dataset.entity_type;
var entity_id = parentElement.dataset.entity_id;
var entity_name = parentElement.dataset.entity_name;
entity_id = parseInt(entity_id);
key = "marked_for_merge_" + entity_type;
var current_stored = getStoredList(key);
current_stored.push(entity_id); current_stored.push(entity_id);
current_stored = [...new Set(current_stored)]; storeList(key,current_stored)
lcst.setItem(key,current_stored); //this already formats it correctly
notify("Marked " + entity_name + " for merge","Currently " + current_stored.length + " marked!") notify("Marked " + entity_name + " for merge","Currently " + current_stored.length + " marked!")
showValidMergeIcons();
toggleMergeIcons(parentElement);
}
function unmarkForMerge(element) {
const parentElement = element.closest('[data-entity_id]');
var entity_type = parentElement.dataset.entity_type;
var entity_id = parentElement.dataset.entity_id;
var entity_name = parentElement.dataset.entity_name;
entity_id = parseInt(entity_id);
var key = "marked_for_merge_" + entity_type;
var current_stored = getStoredList(key);
if (current_stored.indexOf(entity_id) > -1) {
current_stored.splice(current_stored.indexOf(entity_id),1);
storeList(key,current_stored);
notify("Unmarked " + entity_name + " from merge","Currently " + current_stored.length + " marked!")
toggleMergeIcons(parentElement);
}
else {
//notify(entity_name + " was not marked!","")
}
} }
function markForAssociate(element) { function markForAssociate(element) {
console.log(element);
const parentElement = element.closest('[data-entity_id]'); const parentElement = element.closest('[data-entity_id]');
console.log(parentElement);
// use local element for entity data, otherwise use from global scope (on entity info page) var entity_type = parentElement.dataset.entity_type;
var l_entity_type = parentElement ? parentElement.dataset.entity_type : entity_type; var entity_id = parentElement.dataset.entity_id;
var l_entity_id = parentElement ? parentElement.dataset.entity_id : entity_id; var entity_name = parentElement.dataset.entity_name;
var l_entity_name = parentElement ? parentElement.dataset.entity_name : entity_name; entity_id = parseInt(entity_id);
l_entity_id = parseInt(l_entity_id);
const lcst = window.sessionStorage; var key = "marked_for_associate_" + entity_type;
var key = "marked_for_associate_" + l_entity_type; var current_stored = getStoredList(key);
var current_stored = (lcst.getItem(key) || '').split(","); current_stored.push(entity_id);
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x)); storeList(key,current_stored);
current_stored.push(l_entity_id);
current_stored = [...new Set(current_stored)]; notify("Marked " + entity_name + " to add to " + associate_targets[entity_type].join(" or "),"Currently " + current_stored.length + " marked!")
lcst.setItem(key,current_stored); //this already formats it correctly
var whattoadd = ((l_entity_type == 'track') ? "Artists or Album" : "Artists") toggleAssociationIcons(parentElement);
notify("Marked " + l_entity_name + " to add " + whattoadd,"Currently " + current_stored.length + " marked!")
if (!parentElement) {
showValidMergeIcons();
}
else {
toggleAssociationIcons(parentElement);
}
} }
function umarkForAssociate(element) { function umarkForAssociate(element) {
const parentElement = element.closest('[data-entity_id]'); const parentElement = element.closest('[data-entity_id]');
// use local element for entity data, otherwise use from global scope (on entity info page)
var l_entity_type = parentElement ? parentElement.dataset.entity_type : entity_type;
var l_entity_id = parentElement ? parentElement.dataset.entity_id : entity_id;
var l_entity_name = parentElement ? parentElement.dataset.entity_name : entity_name;
l_entity_id = parseInt(l_entity_id);
const lcst = window.sessionStorage; var entity_type = parentElement.dataset.entity_type;
var key = "marked_for_associate_" + l_entity_type; var entity_id = parentElement.dataset.entity_id;
var current_stored = (lcst.getItem(key) || '').split(","); var entity_name = parentElement.dataset.entity_name;
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x)); entity_id = parseInt(entity_id);
if (current_stored.indexOf(l_entity_id) > -1) { var key = "marked_for_associate_" + entity_type;
current_stored.splice(current_stored.indexOf(l_entity_id),1); var current_stored = getStoredList(key);
current_stored = [...new Set(current_stored)];
lcst.setItem(key,current_stored); //this already formats it correctly if (current_stored.indexOf(entity_id) > -1) {
var whattoadd = ((l_entity_type == 'track') ? "Artists or Album" : "Artists") current_stored.splice(current_stored.indexOf(entity_id),1);
notify("Unmarked " + l_entity_name + " to add " + whattoadd,"Currently " + current_stored.length + " marked!") storeList(key,current_stored);
if (!parentElement) {
showValidMergeIcons(); notify("Unmarked " + entity_name + " from association with " + associate_targets[entity_type].join(" or "),"Currently " + current_stored.length + " marked!")
}
else { toggleAssociationIcons(parentElement);
toggleAssociationIcons(parentElement);
}
} }
else { else {
notify(entity_name + " was not marked!","") //notify(entity_name + " was not marked!","")
} }
} }
@ -376,23 +307,78 @@ function toggleAssociationIcons(element) {
var entity_id = element.dataset.entity_id; var entity_id = element.dataset.entity_id;
entity_id = parseInt(entity_id); entity_id = parseInt(entity_id);
const lcst = window.sessionStorage;
var key = "marked_for_associate_" + entity_type; var key = "marked_for_associate_" + entity_type;
var current_stored = (lcst.getItem(key) || '').split(","); var current_stored = getStoredList(key);
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
if (current_stored.indexOf(entity_id) > -1) { if (current_stored.indexOf(entity_id) > -1) {
element.classList.add('marked'); element.classList.add('marked_for_associate');
} else { } else {
element.classList.remove('marked'); element.classList.remove('marked_for_associate');
}
if (current_stored.length > 0) {
element.classList.add('somethingmarked_for_associate');
}
else {
element.classList.remove('somethingmarked_for_associate');
}
var sourcetypes = associate_sources[entity_type];
var sourcelist = [];
for (var src of sourcetypes) {
var key = "marked_for_associate_" + src;
sourcelist = sourcelist.concat(getStoredList(key));
}
if (sourcelist.length > 0) {
element.classList.add('sources_marked_for_associate');
}
else {
element.classList.remove('sources_marked_for_associate');
}
}
function toggleMergeIcons(element) {
var entity_type = element.dataset.entity_type;
var entity_id = element.dataset.entity_id;
entity_id = parseInt(entity_id);
var key = "marked_for_merge_" + entity_type;
var current_stored = getStoredList(key);
if (current_stored.indexOf(entity_id) > -1) {
element.classList.add('marked_for_merge');
} else {
element.classList.remove('marked_for_merge');
}
if (current_stored.length > 0) {
element.classList.add('somethingmarked_for_merge');
}
else {
element.classList.remove('somethingmarked_for_merge');
} }
} }
document.addEventListener('DOMContentLoaded',function(){
var listrows = document.getElementsByClassName('listrow');
for (var row of listrows) {
toggleAssociationIcons(row);
toggleMergeIcons(row); //just for the coloring, no icons
}
var topbars = document.getElementsByClassName('iconsubset');
for (var bar of topbars) {
toggleAssociationIcons(bar);
toggleMergeIcons(bar);
}
})
function merge() { function merge() {
const lcst = window.sessionStorage;
var key = "marked_for_merge_" + entity_type; var key = "marked_for_merge_" + entity_type;
var current_stored = lcst.getItem(key).split(","); var current_stored = getStoredList(key);
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
callback_func = function(req){ callback_func = function(req){
if (req.status == 200) { if (req.status == 200) {
@ -415,26 +401,28 @@ function merge() {
json=true json=true
); );
lcst.removeItem(key); storeList(key,[]);
} }
function associate() { function associate(element) {
const lcst = window.sessionStorage; const parentElement = element.closest('[data-entity_id]');
var target_entity_types = {artist:['album','track'], album:['track']}; var entity_type = parentElement.dataset.entity_type;
var entity_id = parentElement.dataset.entity_id;
entity_id = parseInt(entity_id);
var requests_todo = 0; var requests_todo = 0;
for (var target_entity_type of target_entity_types[entity_type]) { for (var target_entity_type of associate_sources[entity_type]) {
var key = "marked_for_associate_" + target_entity_type; var key = "marked_for_associate_" + target_entity_type;
var current_stored = (lcst.getItem(key) || '').split(","); var current_stored = getStoredList(key);
current_stored = current_stored.filter((x)=>x).map((x)=>parseInt(x));
if (current_stored.length != 0) { if (current_stored.length != 0) {
requests_todo += 1; requests_todo += 1;
callback_func = function(req){ callback_func = function(req){
if (req.status == 200) { if (req.status == 200) {
showValidMergeIcons(); toggleAssociationIcons(parentElement);
notifyCallback(req); notifyCallback(req);
requests_todo -= 1; requests_todo -= 1;
if (requests_todo == 0) { if (requests_todo == 0) {
@ -458,24 +446,30 @@ function associate() {
json=true json=true
); );
lcst.removeItem(key); storeList(key,[]);
} }
} }
} }
function cancelMerge() { function cancelMerge(element) {
const lcst = window.sessionStorage; const parentElement = element.closest('[data-entity_id]');
var entity_type = parentElement.dataset.entity_type;
var key = "marked_for_merge_" + entity_type; var key = "marked_for_merge_" + entity_type;
lcst.setItem(key,[]); storeList(key,[])
showValidMergeIcons(); toggleMergeIcons(parentElement);
notify("Cancelled merge!","") notify("Cancelled " + entity_type + " merge!","")
} }
function cancelAssociate() { function cancelAssociate(element) {
const lcst = window.sessionStorage; const parentElement = element.closest('[data-entity_id]');
var entity_type = parentElement.dataset.entity_type;
var key = "marked_for_associate_" + entity_type; var key = "marked_for_associate_" + entity_type;
lcst.setItem(key,[]); storeList(key,[])
showValidMergeIcons(); toggleAssociationIcons(parentElement);
notify("Cancelled association!","") notify("Cancelled " + entity_type + " association!","")
} }