Add the clipboard tray where users manage their photo clipboard.

Appearing on search and album pages, the tray is where you can
remove items from your clipboard without having to click on its
checkbox -- that photo card may not even be on the current page.
This commit is contained in:
voussoir 2017-12-16 03:47:54 -08:00
parent 0b5f736574
commit 443d93ce18
7 changed files with 143 additions and 15 deletions

View file

@ -54,6 +54,7 @@ If you are interested in helping, please raise an issue before making any pull r
- Use browser localstorage to act as a clipboard for holding photos, so that you can select them on one tab and move them into an album on another, etc. - Use browser localstorage to act as a clipboard for holding photos, so that you can select them on one tab and move them into an album on another, etc.
- Perhaps instead of actually deleting objects, they should just have a `deleted` flag, to make easy restoration possible. Also consider regrouping the children of restored Groupables if those children haven't already been reassigned somewhere else. - Perhaps instead of actually deleting objects, they should just have a `deleted` flag, to make easy restoration possible. Also consider regrouping the children of restored Groupables if those children haven't already been reassigned somewhere else.
- Add a new table to store permanent history of add/remove of tags on photos, so that accidents or trolling can be reversed. - Add a new table to store permanent history of add/remove of tags on photos, so that accidents or trolling can be reversed.
- Currently, the photo clipboard only stores IDs and therefore when we construct the clipboard tray elements we cannot provide more rich information like filename, the user is only presented with a list of IDs which they probably don't care about. Should the localstorage cache some other more user-friendly information?
### To do list: User permissions ### To do list: User permissions
Here are some thoughts about the kinds of features that need to exist within the permission system. I don't know how I'll actually manage it just yet. Possibly a `permissions` table in the database with `user_id | permission` where `permission` is some reliably-formatted string. Here are some thoughts about the kinds of features that need to exist within the permission system. I don't know how I'll actually manage it just yet. Possibly a `permissions` table in the database with `user_id | permission` where `permission` is some reliably-formatted string.

View file

@ -284,3 +284,30 @@ is hovered over.
{ {
background-color: #faa; background-color: #faa;
} }
#clipboard_tray
{
display: flex;
flex-direction: column;
position: fixed;
right: 8px;
bottom: 8px;
z-index: 10;
max-height: 80%;
min-width: 150px;
padding: 2px;
background-color: #ffffd4;
}
#clipboard_tray_expandbutton
{
flex: 1 0 auto;
width: 100%;
}
#clipboard_tray_body
{
width: 300px;
overflow-y: auto;
}

View file

@ -292,6 +292,14 @@ function create_album_and_follow(parent)
post(url, data, receive_callback); post(url, data, receive_callback);
} }
function delete_all_children(element)
{
while (element.firstChild)
{
element.removeChild(element.firstChild);
}
}
function entry_with_history_hook(box, button) function entry_with_history_hook(box, button)
{ {
//console.log(event.keyCode); //console.log(event.keyCode);

View file

@ -1,5 +1,7 @@
var photo_clipboard = new Set(); var photo_clipboard = new Set();
////////////////////////////////////////////////////////////////////////////////
//--LOAD-SAVE-----------------------------------------------------------------//
function load_photo_clipboard(event) function load_photo_clipboard(event)
{ {
console.log("Loading photo clipboard"); console.log("Loading photo clipboard");
@ -15,9 +17,6 @@ function load_photo_clipboard(event)
{ {
photo_clipboard = new Set(JSON.parse(stored)); photo_clipboard = new Set(JSON.parse(stored));
} }
var photo_divs = Array.from(document.getElementsByClassName("photo_card"));
photo_divs.forEach(apply_check);
return photo_clipboard; return photo_clipboard;
} }
@ -26,12 +25,16 @@ function save_photo_clipboard()
console.log("Saving photo clipboard"); console.log("Saving photo clipboard");
var serialized = JSON.stringify(Array.from(photo_clipboard)); var serialized = JSON.stringify(Array.from(photo_clipboard));
localStorage.setItem("photo_clipboard", serialized); localStorage.setItem("photo_clipboard", serialized);
on_storage();
} }
function apply_check(photo_div)
////////////////////////////////////////////////////////////////////////////////
//*-CARD MANAGEMENT-----------------------------------------------------------//
function apply_check(photo_card)
{ {
var checkbox = photo_div.getElementsByClassName("photo_card_selector_checkbox")[0]; var checkbox = photo_card.getElementsByClassName("photo_card_selector_checkbox")[0];
if (photo_clipboard.has(photo_div.dataset.id)) if (photo_clipboard.has(photo_card.dataset.id))
{ {
checkbox.checked = true; checkbox.checked = true;
} }
@ -41,6 +44,12 @@ function apply_check(photo_div)
} }
} }
function update_checked_cards()
{
var photo_divs = Array.from(document.getElementsByClassName("photo_card"));
photo_divs.forEach(apply_check);
}
var previous_photo_select; var previous_photo_select;
function on_photo_select(event) function on_photo_select(event)
{ {
@ -95,9 +104,80 @@ function on_photo_select(event)
save_photo_clipboard(); save_photo_clipboard();
} }
function onpageload()
////////////////////////////////////////////////////////////////////////////////
//--TRAY MANAGEMENT-----------------------------------------------------------//
function toggle_clipboard_tray_collapsed()
{ {
window.addEventListener("storage", load_photo_clipboard, false); var tray_body = document.getElementById("clipboard_tray_body");
load_photo_clipboard(); if (tray_body.classList.contains("hidden") && photo_clipboard.size > 0)
{
tray_body.classList.remove("hidden");
update_clipboard_tray();
}
else
{
tray_body.classList.add("hidden");
}
} }
document.addEventListener("DOMContentLoaded", onpageload);
function on_tray_delete_button(event)
{
var clipboard_line = event.target.parentElement;
var photo_id = clipboard_line.dataset.id;
photo_clipboard.delete(photo_id);
if (photo_clipboard.size == 0)
{
toggle_clipboard_tray_collapsed();
}
save_photo_clipboard();
}
function update_clipboard_tray()
{
var tray_button = document.getElementById("clipboard_tray_expandbutton");
if (tray_button !== null)
{
tray_button.innerText = "Clipboard: " + photo_clipboard.size + " items";
}
var tray_body = document.getElementById("clipboard_tray_body");
if (!clipboard_tray.classList.contains("hidden"))
{
delete_all_children(tray_body);
var photo_ids = Array.from(photo_clipboard);
photo_ids.sort();
for (var i = 0; i < photo_ids.length; i += 1)
{
var clipboard_line = document.createElement("div");
clipboard_line.classList.add("clipboard_tray_line");
clipboard_line.dataset.id = photo_ids[i];
var clipboard_line_delete_button = document.createElement("button");
clipboard_line_delete_button.classList.add("remove_tag_button_perm");
clipboard_line_delete_button.onclick = on_tray_delete_button;
var clipboard_line_link = document.createElement("a");
clipboard_line_link.target = "_blank";
clipboard_line_link.href = "/photo/" + photo_ids[i];
clipboard_line_link.innerText = photo_ids[i];
clipboard_line.appendChild(clipboard_line_delete_button);
clipboard_line.appendChild(clipboard_line_link);
tray_body.appendChild(clipboard_line);
}
}
}
function on_storage()
{
load_photo_clipboard();
update_checked_cards();
update_clipboard_tray();
}
function on_pageload()
{
window.addEventListener("storage", on_storage, false);
on_storage();
}
document.addEventListener("DOMContentLoaded", on_pageload);

View file

@ -3,6 +3,7 @@
<head> <head>
{% import "photo_card.html" as photo_card %} {% import "photo_card.html" as photo_card %}
{% import "header.html" as header %} {% import "header.html" as header %}
{% import "clipboard_tray.html" as clipboard_tray %}
<title>Album {{album.display_name}}</title> <title>Album {{album.display_name}}</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@ -115,6 +116,7 @@ p
{% endif %} {% endif %}
{% endif %} {% endif %}
</p> </p>
{{clipboard_tray.clipboard_tray()}}
</div> </div>
</body> </body>

View file

@ -0,0 +1,11 @@
{% macro clipboard_tray() %}
<div id="clipboard_tray">
<button
id="clipboard_tray_expandbutton"
class="green_button"
onclick="toggle_clipboard_tray_collapsed()"
>Clipboard: 0 items</button>
<div id="clipboard_tray_body" class="hidden">
</div>
</div>
{% endmacro %}

View file

@ -4,6 +4,7 @@
{% import "photo_card.html" as photo_card %} {% import "photo_card.html" as photo_card %}
{% import "header.html" as header %} {% import "header.html" as header %}
{% import "tag_object.html" as tag_object %} {% import "tag_object.html" as tag_object %}
{% import "clipboard_tray.html" as clipboard_tray %}
<title>Search</title> <title>Search</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@ -37,11 +38,6 @@ form
color: #fff; color: #fff;
} }
#left, #right
{
/* Keep the prev-next buttons from scraping the floor */
/*padding-bottom: 30px;*/
}
#left #left
{ {
flex: 1; flex: 1;
@ -60,6 +56,8 @@ form
flex: 1; flex: 1;
padding: 8px; padding: 8px;
/* Keep the prev-next buttons from scraping the floor */
padding-bottom: 30px;
width: auto; width: auto;
} }
@ -296,6 +294,7 @@ form
{{prev_next_buttons()}} {{prev_next_buttons()}}
</div> </div>
{{clipboard_tray.clipboard_tray()}}
</div> </div>
</body> </body>