WIP state

This commit is contained in:
Booteille 2024-12-06 16:38:45 +01:00
parent 4aff334677
commit 3640e22239
No known key found for this signature in database
GPG Key ID: 0AB6C6CA01272646
10 changed files with 143 additions and 103 deletions

View File

@ -32,7 +32,7 @@ fontawesomeSubset(
const ignoreFontsPlugin: esbuild.Plugin = {
name: "file",
setup(build) {
build.onResolve({ filter: /\.woff2|ttf$/ }, (args) => ({
build.onResolve({ filter: /\.woff2|ttf$/ }, () => ({
external: true,
}));
},

View File

@ -18,7 +18,7 @@ sub new_invite {
if ($c->is_user_authenticated) {
my $mail_attr = $c->config('invitations')->{'mail_attr'} // 'mail';
my $max_expire_at = $c->config('invitations')->{'max_invitation_expiration_delay'} // 30;
my $send_with_user_email = defined $c->config('invitations')->{'send_invitation_with_ldap_user_mail'};
my $send_with_user_email = defined $c->config('invitations')->{'send_invitation_with_ldap_user_mail'} && $c->config('invitations')->{'send_invitation_with_ldap_user_mail'} eq 1;
$c->render(
template => 'invitations/invite',
max_expire_at => $max_expire_at,

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@ const addItem = (item) => {
const files = JSON.parse(localStorage.getItem(`${prefix}files`)) || [];
files.push(item);
localStorage.setItem(`${prefix}files`, JSON.stringify(files));
};
@ -195,8 +196,6 @@ const populateFilesTable = () => {
itemsTableDOM.append(itemDOM);
console.debug(file.short, file.token);
fetch(counterURL, {
method: "POST",
headers: {

View File

@ -7,6 +7,22 @@ const entityMap = {
"/": "/",
};
export const addToast = (message, type) => {
const notification = document
.querySelector("template#notification")
.content.cloneNode(true).children[0];
notification.classList.add(`is-${type}`);
notification.querySelector(".message").innerText = message;
document.getElementById("notifications-container").append(notification);
setTimeout(() => {
notification.remove();
}, 3500);
};
export const copyToClipboard = async (text) => {
try {
if (navigator.clipboard) {
@ -50,21 +66,11 @@ export const formatDate = (unixTimestamp) =>
minute: "2-digit",
});
// export const notify = (message, type) => {
// const notification = document
// .querySelector("template#notification")
// .content.cloneNode(true).children[0];
// notification.classList.add(`is-${type}`);
// notification.querySelector(".message").innerText = message;
// document.getElementById("notifications-container").append(notification);
// setTimeout(() => {
// notification.remove();
// }, 3500);
// };
export const hideNode = (node) => {
if (node) {
node.classList.add("is-hidden");
}
};
export const notify = (title, body) => {
if (isSecureContext) {
@ -86,6 +92,12 @@ export const notify = (title, body) => {
}
};
export const showNode = (node) => {
if (node) {
node.classList.remove("is-hidden");
}
};
export const uuidv4 = () =>
"10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) =>
(

View File

@ -1,21 +1,23 @@
import { filesize } from "~/lib/filesize.esm.min.js";
import { addToast, hideNode, showNode } from "~/lib/utils.js";
const updateButtonsStatus = () => {
const targetSelectionDOM = document.querySelectorAll(".target-selection");
if (
document.querySelectorAll(".column.selection .checkbox input:checked")
.length > 0
document.querySelectorAll(".selection .checkbox input:checked").length > 0
) {
targetSelectionDOM.forEach((node) => (node.disabled = false));
targetSelectionDOM.forEach((node) => {
node.disabled = false;
});
} else {
targetSelectionDOM.forEach((node) => (node.disabled = true));
}
};
const invertSelection = () => {
document.querySelectorAll(".item .column.selection input").forEach((node) => {
node.click();
const updateSelection = (event) => {
document.querySelectorAll(".item .checkbox input").forEach((node) => {
node.checked = event.target.checked;
});
updateButtonsStatus();
@ -28,12 +30,12 @@ const toggleHidden = () => {
".item[data-visibility='0']"
);
if (invitationsListDOM.getAttribute("data-visibility") === "hidden") {
if (invitationsListDOM.dataset.visibility === "hidden") {
toggleButtonDOM.innerText = i18n.hideText;
itemsHiddenDOM.forEach((item) => showNode(item));
invitationsListDOM.setAttribute("data-visibility", "shown");
invitationsListDOM.dataset.visibility = "shown";
} else {
toggleButtonDOM.innerText = i18n.showText;
@ -47,7 +49,7 @@ const toggleHidden = () => {
}
});
invitationsListDOM.setAttribute("data-visibility", "hidden");
invitationsListDOM.dataset.visibility = "hidden";
}
};
@ -70,7 +72,7 @@ const deleteInvitation = () => {
})
.then((data) => {
if (data.success) {
data.tokens.forEach((t) => {
data.success.forEach((t) => {
addToast(t.msg, "success");
document.getElementById(`row-${t.token}`).remove();
});
@ -114,13 +116,12 @@ const resendInvitation = () => {
})
.then((data) => {
if (data.success) {
data.tokens.forEach((t) => {
data.success.forEach((t) => {
const itemDOM = document.getElementById(`row-${t.token}`);
itemDOM.querySelector(".column.expiration-date").innerText =
t.expires;
itemDOM.querySelector(".expiration-date").innerText = t.expires;
itemDOM.querySelector(".column.selection input").click();
itemDOM.querySelector(".selection input").click();
addToast(t.msg, "success");
});
@ -158,22 +159,19 @@ const toggleVisibility = () => {
if (t.show) {
itemDOM.setAttribute("data-visibility", 1);
showNode(itemDOM);
itemDOM
.querySelector(".column.selection .icon.hide-source")
.remove();
itemDOM.querySelector(".selection .icon.hide-source").remove();
} else {
itemDOM.setAttribute("data-visibility", 0);
if (
document
.querySelector(".invitations-list")
.getAttribute("data-visibility") === "hidden"
document.querySelector(".invitations-list").dataset.visibility ===
"hidden"
) {
hideNode(itemDOM);
}
itemDOM
.querySelector(".column.selection")
.querySelector(".selection")
.appendChild(
document
.querySelector("template#icon-hide-source")
@ -181,7 +179,7 @@ const toggleVisibility = () => {
);
}
itemDOM.querySelector(".column.selection input").click();
itemDOM.querySelector(".selection input").click();
});
updateButtonsStatus();
@ -196,10 +194,8 @@ const getTokensBody = () => {
const tokens = new URLSearchParams();
document
.querySelectorAll(".column.selection input:checked")
.forEach((item) =>
tokens.append("tokens[]", item.getAttribute("data-token"))
);
.querySelectorAll(".selection input:checked")
.forEach((item) => tokens.append("tokens[]", item.dataset.token));
return tokens;
};
@ -212,10 +208,10 @@ const fillModal = (event) => {
modalDOM.querySelector(".files-list").replaceChildren();
modalDOM.querySelector("h1").innerText = i18n.listFiles
.replace("XX1", buttonDOM.getAttribute("data-token"))
.replace("XX2", buttonDOM.getAttribute("data-guest"));
.replace("XX1", buttonDOM.dataset.token)
.replace("XX2", buttonDOM.dataset.guest);
const files = JSON.parse(buttonDOM.getAttribute("data-files")) || [];
const files = JSON.parse(buttonDOM.dataset.files) || [];
const itemList = new DocumentFragment();
files.forEach((file) => {
@ -254,10 +250,10 @@ document.addEventListener("DOMContentLoaded", () => {
};
document
.querySelectorAll(".column.selection input")
.querySelectorAll(".selection input")
.forEach((node) => (node.onclick = updateButtonsStatus));
document.querySelector(".action-invert-selection").onclick = invertSelection;
document.getElementById("action-select-all").onclick = updateSelection;
document.querySelector(".action-toggle-hidden").onclick = toggleHidden;

View File

@ -252,6 +252,7 @@ document.addEventListener("DOMContentLoaded", () => {
// Add the file to localStorage
if (!isGuest) {
console.debug(job.lufiFile.keys.server);
addItem(
job.lufiFile.name,
job.lufiFile.downloadUrl(),

View File

@ -53,7 +53,7 @@
<thead>
<tr>
<th class="has-text-centered">
<span><%= l('Selection') %></span>
<div><%= l('Selection') %></div>
<div class="checkbox">
<input type="checkbox" id="action-select-all">
</div>

View File

@ -25,7 +25,7 @@
<p>
<%= l('You can invite someone to send you files through this Lufi instance even if they dont have an account on it.') %>
</p>
% if (stash('send_invitation_with_ldap_user_email')) {
% if (stash('send_with_user_email')) {
<p>
<%= l('The invitation mail will be send from your email address (%1).', stash('user_mail')) %>
</p>

View File

@ -1,29 +1,40 @@
% use Number::Bytes::Human qw(format_bytes);
% my $lang = $self->get_date_lang();
<section class="my-invitations-section">
<h1><%= l('My invitations') %></h1>
<div class="box">
<h1 class="title is-1"><%= l('My invitations') %></h1>
<div class="message is-info">
<div class="message-body">
<p>
<%= l('Rows in purple mean that the invitations have expired.') %>
</p>
<div class="actions-buttons">
<button href="#" class="button action-invert-selection"><%= l('Invert selection') %></button>
</div>
</div>
<div class="buttons">
<button href="#" class="button action-toggle-hidden"><%= l('Show hidden invitations') %></button>
<button href="#" class="button target-selection action-delete-invitation" disabled="disabled"=><%= l('Delete') %></button>
<button href="#" class="button target-selection action-resend-invitation" disabled="disabled"><%= l('Resend invitation mail') %></button>
<button href="#" class="button target-selection action-toggle-visibility" disabled="disabled"><%= l('Toggle visibility') %></button>
</div>
<table>
<div class="table-container">
<table class="table is-stripped is-hoverable">
<thead>
<tr>
<th><%= l('Selection') %></th>
<th><%= l('Guest mail') %></th>
<th><%= l('URL') %></th>
<th><%= l('Created at') %></th>
<th><%= l('Expire at') %></th>
<th><%= l('Files sent at') %></th>
<th><%= l('Files') %></th>
<th class="has-text-centered">
<div><%= l('Selection') %></div>
<div class="checkbox">
<input type="checkbox" id="action-select-all">
</div>
</th>
<th class="has-text-centered"><%= l('Guest mail') %></th>
<th class="has-text-centered"><%= l('URL') %></th>
<th class="has-text-centered"><%= l('Created at') %></th>
<th class="has-text-centered"><%= l('Expire at') %></th>
<th class="has-text-centered"><%= l('Files sent at') %></th>
<th class="has-text-centered"><%= l('Files') %></th>
</tr>
</thead>
<tbody class="invitations-list" data-visibility="hidden">
@ -33,23 +44,23 @@
% return if $e->deleted;
% my $class = '';
% $class = 'deleted' unless $e->is_valid;
% $class .= ' hidden' unless $e->show_in_list;
% $class .= ' is-hidden' unless $e->show_in_list;
<tr id="row-<%= $e->token %>" class="item <%= $class %>" aria-hidden="<%= ($e->show_in_list) ? 'true' : 'false' %>" data-visibility="<%= ($e->show_in_list) ? 1 : 0 %>">
<td class="column selection">
<div class="checkbox input-selection">
<input type="checkbox" data-token="<%= $e->token %>" aria-label="Select">
<td class="selection is-vcentered has-text-centered">
<div class="checkbox input-delete-on-first-view">
<input type="checkbox" data-token="<%= $e->token %>" autocomplete="off" aria-label="Select">
% unless ($e->show_in_list) {
<span class="icon hide-source" title="<%= l('This invitation is normally hidden') %>"></span>
<span class="icon fas fa-eye-slash" title="<%= l('This invitation is normally hidden') %>"></span>
% }
</div>
</td>
<td class="column mail"><%= $e->guest_mail %></td>
<td class="column url"><%= url_for('guest', token => $e->token)->to_abs %></td>
<td class="column creation-date"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->created_at) %></td>
<td class="column expiration-date"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->expire_at) %></td>
<td class="column files-sent-data"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->files_sent_at) if $e->files_sent_at %></td>
<td class="column actions">
<td class="mail is-vcentered"><%= $e->guest_mail %></td>
<td class="url is-vcentered"><%= url_for('guest', token => $e->token)->to_abs %></td>
<td class="creation-date is-vcentered"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->created_at) %></td>
<td class="expiration-date is-vcentered"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->expire_at) %></td>
<td class="files-sent-data is-vcentered"><%= $lang->time2str(l('%A %d %B %Y at %T'), $e->files_sent_at) if $e->files_sent_at %></td>
<td class="actions is-vcentered">
% if ($e->files) {
<a
class="button modal-button action-files-info"
@ -66,6 +77,7 @@
% });
</tbody>
</table>
</div>
<dialog class="modal files-info">
<section>
@ -82,10 +94,30 @@
</footer>
</section>
</dialog>
</section>
</div>
<div id="modal-language-selector" class="modal files-info">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head is-shadowless">
<p class="modal-card-title">
<%= l('Files sent in invitation XX1 by XX2') %>
<button class="delete is-pulled-right" aria-label="close"></button>
</p>
</header>
<section class="modal-card-body">
<ul class="files-list">
</li>
</section>
<footer class="modal-card-foot"></footer>
</div>
</div>
<template id="icon-hide-source">
<span class="icon hide-source" title="<%= l('This invitation is normally hidden') %>"></span>
<span class="icon fas fa-eye-slash" title="<%= l('This invitation is normally hidden') %>"></span>
</template>
<template id="item">
@ -109,4 +141,4 @@ const deleteURL = '<%= url_for('invite_list_delete') %>';
const resendURL = '<%= url_for('invite_list_resend') %>';
const toggleURL = '<%= url_for('invite_list_visibility') %>';
</script>
%= javascript '/js/min.lufi-list-invitations.js', type => 'module', defer => "true"
%= javascript '/js/minified/list-invitations.min.js', type => 'module', defer => "true"