etiquette/frontends/etiquette_flask/templates/clipboard.html
Ethan Dalool 4c65ccaf68 Big! Liberate Groupables from strict heirarchy. Multiple parents.
I found that the strict heirarchy was not satisfying the situation
where one tag is the intersection of two others, but we can only
pick one as the parent

For example, does red_jacket belong under clothes.red_clothes or
clothes.jackets? A search for "red_clothes AND jackets" might
give us someone wearing red pants and a black jacket, so this
definitely needs to be a separate tag, but picking only one
parent for it is not sufficient. Now, a search for red_clothes
and a search for jackets will both find our red_jacket photo.

The change also applies to Albums because why not, and I'm sure
a similar case can be made.

Unfortunately this means tags no longer have one true qualname.
The concept of qualnames has not been completely phased out but
it's in progress.

This commit is very big because I was not sure for a long time
whether to go through with it, and so much stuff had to change
that I don't want to go back and figure out what could be grouped
together.
2018-07-20 13:09:06 -07:00

339 lines
8.7 KiB
HTML

<!DOCTYPE html5>
<html>
<head>
{% import "header.html" as header %}
{% import "clipboard_tray.html" as clipboard_tray %}
<title>Clipboard</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="/static/css/common.css">
<link rel="stylesheet" href="/static/css/photo_card.css">
<link rel="stylesheet" href="/static/css/clipboard_tray.css">
<script src="/static/js/common.js"></script>
<script src="/static/js/hotkeys.js"></script>
<script src="/static/js/photoclipboard.js"></script>
<style>
body
{
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 1fr 300px;
grid-template-areas:
"header header"
"left right";
}
#header
{
grid-area: header;
}
#left
{
word-break: break-word;
grid-area: left;
}
#right
{
position: fixed;
right: 8px;
bottom: 8px;
top: 30px;
width: 300px;
grid-area: right;
display: grid;
grid-template-rows: 75px 75px 75px 75px auto;
grid-template-areas:
"add_tag_area"
"remove_tag_area"
"refresh_metadata_area"
"searchhidden_area"
"message_area";
background-color: rgba(0, 0, 0, 0.1);
}
#add_tag_area
{
grid-area: add_tag_area;
margin: auto;
}
#remove_tag_area
{
grid-area: remove_tag_area;
margin: auto;
}
#refresh_metadata_area
{
grid-area: refresh_metadata_area;
margin: auto;
}
#searchhidden_area
{
grid-area: searchhidden_area;
margin: auto;
}
#message_area
{
grid-area: message_area;
margin: 8px;
}
</style>
</head>
<body>
{{header.make_header(session=session)}}
<div id="left">
<span>The clipboard contains <span class="clipboard_count">0</span> items.</span>
<button id="clear_clipboard_button" class="red_button" onclick="clear_photo_clipboard()">Clear it.</button>
<div id="photo_card_holder">
</div>
</div>
<div id="right">
<div id="add_tag_area">
<input type="text" id="add_tag_textbox">
<button class="add_tag_button green_button" id="add_tag_button" onclick="submit_add_tag(add_remove_callback);">Add tag</button>
</div>
<div id="remove_tag_area">
<input type="text" id="remove_tag_textbox">
<button class="red_button" id="remove_tag_button" onclick="submit_remove_tag(add_remove_callback);">Remove tag</button>
</div>
<div id="refresh_metadata_area">
<button class="green_button" id="refresh_metadata_button" onclick="submit_refresh_metadata(refresh_metadata_callback);">Refresh metadata</button>
</div>
<div id="searchhidden_area">
<span>
<button class="yellow_button" id="set_searchhidden_button" onclick="submit_set_searchhidden(searchhidden_callback)">Searchhide</button>
<button class="yellow_button" id="unset_searchhidden_button" onclick="submit_unset_searchhidden(searchhidden_callback)">Unhide</button>
</span>
</div>
<div id="message_area">
</div>
</div>
</body>
<script type="text/javascript">
var divs = {};
var needed = new Set();
var holder = document.getElementById("photo_card_holder");
var add_box = document.getElementById("add_tag_textbox");
var add_button = document.getElementById("add_tag_button");
add_box.addEventListener("keyup", entry_with_history_hook);
bind_box_to_button(add_box, add_button);
var remove_box = document.getElementById("remove_tag_textbox");
var remove_button = document.getElementById("remove_tag_button");
remove_box.addEventListener("keyup", entry_with_history_hook);
bind_box_to_button(remove_box, remove_button);
function recalculate_needed()
{
needed = new Set();
photo_clipboard.forEach(function(photo_id)
{
if (!(photo_id in divs))
{
needed.add(photo_id);
}
});
}
function refresh_divs()
{
for (var photo_id in divs)
{
var photo_div = divs[photo_id];
var should_keep = photo_clipboard.has(photo_id);
var on_page = holder.contains(photo_div);
if (on_page && !should_keep)
{
holder.removeChild(photo_div)
}
if (!on_page && should_keep)
{
holder.appendChild(photo_div)
}
}
}
function request_more_divs()
{
if (needed.size == 0)
{
return;
}
var url = "/batch/photos/photo_card";
var data = new FormData();
var photo_ids = Array.from(needed).join(",");
data.append("photo_ids", photo_ids);
function callback(response)
{
if (response["meta"]["status"] !== 200)
{
return;
}
response = response["data"];
var holder = document.getElementById("photo_card_holder");
for (photo_id in response)
{
photo_div = html_to_element(response[photo_id]);
divs[photo_id] = photo_div;
needed.delete(photo_id)
holder.appendChild(photo_div);
}
apply_check_all();
}
post(url, data, callback);
}
function myhook()
{
recalculate_needed();
request_more_divs();
refresh_divs();
}
on_clipboard_load_hooks.push(myhook);
on_clipboard_save_hooks.push(myhook);
function submit_add_tag(callback)
{
var box = document.getElementById("add_tag_textbox");
var tagname = box.value.trim();
if (! tagname)
{return}
box.value = "";
return submit_add_remove_tag("add", tagname, callback);
}
function submit_remove_tag(callback)
{
var box = document.getElementById("remove_tag_textbox");
var tagname = box.value.trim();
if (! tagname)
{return}
box.value = "";
return submit_add_remove_tag("remove", tagname, callback);
}
function submit_add_remove_tag(action, tagname, callback)
{
if (photo_clipboard.size == 0)
{return;}
var url = "/batch/photos/" + action + "_tag";
var photo_ids = Array.from(photo_clipboard).join(",");
var data = new FormData();
data.append("photo_ids", photo_ids);
data.append("tagname", tagname);
post(url, data, callback);
}
function add_remove_callback(response)
{
response = response["data"];
var tagname = response["tagname"];
var message_area = document.getElementById("message_area");
var message_positivity;
var message_text;
if ("error_type" in response)
{
message_positivity = "message_negative";
message_text = response["error_message"];
}
else if ("action" in response)
{
var action = response["action"];
message_positivity = "message_positive";
if (action == "add")
{message_text = "Added tag " + tagname;}
else if (action == "remove")
{message_text = "Removed tag " + tagname;}
}
create_message_bubble(message_area, message_positivity, message_text, 8000);
}
var refresh_in_progress = false;
function submit_refresh_metadata(callback)
{
if (refresh_in_progress)
{return;}
if (photo_clipboard.size == 0)
{return;}
var url = "/batch/photos/refresh_metadata";
var photo_ids = Array.from(photo_clipboard).join(",");
var data = new FormData();
data.append("photo_ids", photo_ids);
refresh_in_progress = true;
post(url, data, callback);
}
function refresh_metadata_callback(response)
{
response = response["data"];
refresh_in_progress = false;
if ("error_type" in response)
{
var message_area = document.getElementById("message_area");
var message_positivity = "message_negative";
var message_text = response["error_message"];
create_message_bubble(message_area, message_positivity, message_text, 8000);
}
else
{
location.reload();
}
}
function searchhidden_callback(response)
{
response = response["data"];
var message_area = document.getElementById("message_area");
var message_positivity;
var message_text;
if ("error_type" in response)
{
message_positivity = "message_negative";
message_text = response["error_message"];
}
else
{
message_positivity = "message_positive";
message_text = "Success."
}
create_message_bubble(message_area, message_positivity, message_text, 8000);
}
function submit_set_searchhidden(callback)
{
if (photo_clipboard.size == 0)
{return;}
var url = "/batch/photos/set_searchhidden";
var data = new FormData();
var photo_ids = Array.from(photo_clipboard).join(",");
data.append("photo_ids", photo_ids);
post(url, data, callback);
}
function submit_unset_searchhidden(callback)
{
if (photo_clipboard.size == 0)
{return;}
var url = "/batch/photos/unset_searchhidden";
var data = new FormData();
var photo_ids = Array.from(photo_clipboard).join(",");
data.append("photo_ids", photo_ids);
post(url, data, callback);
}
</script>
</html>