Refactor to reduce lines

This commit is contained in:
2026-02-18 00:27:59 +00:00
parent a59a0b0329
commit 521692c458
6 changed files with 596 additions and 816 deletions

View File

@@ -27,74 +27,63 @@
<script src="{% static 'js/gridstack-all.js' %}"></script>
<script defer src="{% static 'js/magnet.min.js' %}"></script>
<script>
document.addEventListener("restore-scroll", function(event) {
var scrollpos = localStorage.getItem('scrollpos');
document.addEventListener("restore-scroll", function () {
var scrollpos = localStorage.getItem("scrollpos");
if (scrollpos) {
window.scrollTo(0, scrollpos)
};
window.scrollTo(0, scrollpos);
}
});
document.addEventListener("htmx:beforeSwap", function(event) {
localStorage.setItem('scrollpos', window.scrollY);
document.addEventListener("htmx:beforeSwap", function () {
localStorage.setItem("scrollpos", window.scrollY);
});
</script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
// Add a click event on each of them
$navbarBurgers.forEach( el => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target;
const $target = document.getElementById(target);
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".navbar-burger").forEach(function (el) {
el.addEventListener("click", function () {
var target = document.getElementById(el.dataset.target);
el.classList.toggle("is-active");
if (target) {
target.classList.toggle("is-active");
}
});
});
const composeLink = document.getElementById('nav-compose-link');
const composeDropdown = document.getElementById('nav-compose-contacts');
let composePreviewLoaded = false;
let composePreviewLoading = false;
if (composeLink && composeDropdown) {
composeLink.addEventListener('mouseenter', () => {
const previewUrl = composeLink.dataset.previewUrl || '';
var composeLink = document.getElementById("nav-compose-link");
var composeDropdown = document.getElementById("nav-compose-contacts");
var composePreviewLoaded = false;
var composePreviewLoading = false;
if (!composeLink || !composeDropdown) {
return;
}
composeLink.addEventListener("mouseenter", function () {
var previewUrl = composeLink.dataset.previewUrl || "";
if (!previewUrl || composePreviewLoaded || composePreviewLoading) {
return;
}
composePreviewLoading = true;
fetch(previewUrl, {
method: 'GET',
credentials: 'same-origin',
headers: { 'HX-Request': 'true' },
method: "GET",
credentials: "same-origin",
headers: { "HX-Request": "true" },
})
.then((response) => {
.then(function (response) {
if (!response.ok) {
throw new Error('Failed contacts preview fetch.');
throw new Error("Failed contacts preview fetch.");
}
return response.text();
})
.then((html) => {
.then(function (html) {
composeDropdown.innerHTML = html;
composePreviewLoaded = true;
})
.catch(() => {
.catch(function () {
composePreviewLoaded = false;
})
.finally(() => {
.finally(function () {
composePreviewLoading = false;
});
});
}
});
</script>
<style>
@@ -112,9 +101,7 @@
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator{
opacity:1
}
@@ -142,15 +129,11 @@
transition: all 0.2s ease-in-out;
}
tr:hover {
cursor:pointer;
background-color:rgba(221, 224, 255, 0.3) !important;
}
a.panel-block {
transition: all 0.2s ease-in-out;
}
tr:hover,
a.panel-block:hover {
cursor:pointer;
background-color:rgba(221, 224, 255, 0.3) !important;
@@ -180,11 +163,9 @@
}
.panel, .box, .modal {
/* background-color:rgba(250, 250, 250, 0.5) !important; */
background-color: var(--modal-color) !important;
}
.modal, .modal.box{
/* background-color:rgba(210, 210, 210, 0.9) !important; */
background-color: var(--background-color) !important;
}
.modal-background{
@@ -218,7 +199,8 @@
background-color:rgba(0, 0, 0, 0.03) !important;
}
.grid-stack-item-content {
.grid-stack-item-content,
.floating-window {
display: flex !important;
flex-direction: column !important;
overflow-x: hidden !important;
@@ -239,11 +221,6 @@
}
.floating-window {
/* background-color:rgba(210, 210, 210, 0.6) !important; */
display: flex !important;
flex-direction: column !important;
overflow-x: hidden !important;
overflow-y: hidden !important;
max-height: 300px;
z-index: 9000;
position: absolute;
@@ -257,8 +234,7 @@
.float-right {
float: right;
padding-right: 5px;
padding-left: 5px;
padding: 0 5px;
}
.grid-stack-item:hover .ui-resizable-handle {
display: block !important;

View File

@@ -819,135 +819,104 @@
(function() {
const personId = "{{ person.id }}";
const canSend = "{{ send_state.can_send|yesno:'1,0' }}" === "1";
function resizeEditableTextareas(root) {
const TAB_NAMES = ["plan_board", "corrections", "engage", "fundamentals", "auto", "ask_ai"];
const defineGlobal = function(name, handler) {
if (typeof window[name] !== "function") {
window[name] = handler;
}
};
const resizeEditableTextareas = function(root) {
if (!root) return;
root.querySelectorAll('textarea[data-editable="1"]').forEach(function(area) {
area.style.height = "auto";
area.style.height = Math.max(area.scrollHeight, 72) + "px";
});
}
window.giaEngageSyncSendOverride = function(pid) {
if (pid !== personId) return;
const forceInput = document.getElementById("engage-force-send-" + pid);
const sendBtn = document.getElementById("engage-send-btn-" + pid);
const force =
!!(window.giaWorkspaceState
&& window.giaWorkspaceState[pid]
&& window.giaWorkspaceState[pid].forceSend);
if (forceInput) {
forceInput.value = force ? "1" : "0";
}
if (sendBtn) {
sendBtn.disabled = !canSend && !force;
}
};
function setActiveTabHiddenFields(tabName) {
const syncActiveTab = function(tabName) {
const root = document.getElementById("mitigation-shell-" + personId);
if (!root) return;
root.querySelectorAll('input[name="active_tab"]').forEach(function(input) {
input.value = tabName;
});
resizeEditableTextareas(root);
};
window.giaEngageSyncSendOverride = function(pid) {
if (pid !== personId) return;
const forceInput = document.getElementById("engage-force-send-" + pid);
const sendBtn = document.getElementById("engage-send-btn-" + pid);
const force = !!(window.giaWorkspaceState && window.giaWorkspaceState[pid] && window.giaWorkspaceState[pid].forceSend);
if (forceInput) forceInput.value = force ? "1" : "0";
if (sendBtn) {
sendBtn.disabled = !canSend && !force;
}
};
window.giaMitigationShowTab = function(pid, tabName) {
if (pid !== personId) return;
["plan_board", "corrections", "engage", "fundamentals", "auto", "ask_ai"].forEach(function(name) {
TAB_NAMES.forEach(function(name) {
const pane = document.getElementById("mitigation-tab-" + personId + "-" + name);
const tab = document.getElementById("mitigation-tab-btn-" + personId + "-" + name);
if (!pane) return;
const active = name === tabName;
pane.style.display = active ? "block" : "none";
if (tab) {
tab.classList.toggle("is-active", active);
}
if (tab) tab.classList.toggle("is-active", active);
});
setActiveTabHiddenFields(tabName);
syncActiveTab(tabName);
};
window.giaMitigationToggleEdit = function(button) {
const form = button.closest("form");
defineGlobal("giaMitigationToggleEdit", function(button) {
const form = button && button.closest ? button.closest("form") : null;
if (!form) return;
const card = form.closest(".mitigation-artifact-card");
const editing = button.dataset.editState === "edit";
const fields = form.querySelectorAll('[data-editable="1"]');
const toggles = form.querySelectorAll('[data-editable-toggle="1"]');
if (!editing) {
fields.forEach(function(field) {
field.removeAttribute("readonly");
});
toggles.forEach(function(field) {
field.removeAttribute("disabled");
});
if (card) {
card.classList.add("is-editing");
}
form.querySelectorAll('[data-editable="1"]').forEach(function(field) { field.removeAttribute("readonly"); });
form.querySelectorAll('[data-editable-toggle="1"]').forEach(function(field) { field.removeAttribute("disabled"); });
const card = form.closest(".mitigation-artifact-card");
if (card) card.classList.add("is-editing");
button.dataset.editState = "edit";
button.classList.remove("is-light");
button.title = "Save";
button.innerHTML = '<span class="icon is-small"><i class="fa-solid fa-check"></i></span>';
resizeEditableTextareas(form);
} else {
return;
}
form.requestSubmit();
}
};
});
window.giaEngageSetAction = function(pid, action) {
if (pid !== personId) return;
defineGlobal("giaEngageSetAction", function(pid, action) {
const actionInput = document.getElementById("engage-action-input-" + pid);
if (actionInput) {
actionInput.value = action;
}
if (action === "send") {
window.giaEngageSyncSendOverride(pid);
}
};
if (actionInput) actionInput.value = action;
if (action === "send") window.giaEngageSyncSendOverride(pid);
});
window.giaEngageAutoPreview = function(pid) {
if (pid !== personId) return;
defineGlobal("giaEngageAutoPreview", function(pid) {
const form = document.getElementById("engage-form-" + pid);
if (!form) return;
window.giaEngageSetAction(pid, "preview");
form.requestSubmit();
};
});
window.giaEngageSetTarget = function(pid, targetId) {
if (pid !== personId) return;
const normalized = String(targetId || "").trim();
const input = document.getElementById("engage-target-input-" + pid);
if (input) {
input.value = normalized;
}
if (input) input.value = normalized;
const parentSelect = document.getElementById("ai-target-select-" + pid);
if (parentSelect && normalized) {
parentSelect.value = normalized;
}
if (parentSelect && normalized) parentSelect.value = normalized;
};
window.giaEngageSelect = function(pid, kind, value, node) {
if (pid !== personId) return;
let inputId = "";
if (kind === "share") {
inputId = "engage-share-input-" + pid;
} else if (kind === "framing") {
inputId = "engage-framing-input-" + pid;
}
defineGlobal("giaEngageSelect", function(pid, kind, value, node) {
const inputId = kind === "share" ? ("engage-share-input-" + pid) : (kind === "framing" ? ("engage-framing-input-" + pid) : "");
const input = inputId ? document.getElementById(inputId) : null;
if (input) {
input.value = value;
}
if (input) input.value = value;
const li = node && node.closest ? node.closest("li") : null;
if (!li) return;
const ul = li.parentElement;
if (!ul) return;
Array.from(ul.children).forEach(function(child) {
child.classList.remove("is-active");
});
if (!li || !li.parentElement) return;
Array.from(li.parentElement.children).forEach(function(child) { child.classList.remove("is-active"); });
li.classList.add("is-active");
window.giaEngageAutoPreview(pid);
};
});
window.giaMitigationShowTab(personId, "{{ active_tab|default:'plan_board' }}");
resizeEditableTextareas(document.getElementById("mitigation-shell-" + personId));

View File

@@ -833,8 +833,14 @@
syncTargetInputs();
});
if (typeof window.giaMitigationShowTab !== "function") {
window.giaMitigationShowTab = function(pid, tabName) {
const defineGlobal = function(name, handler) {
if (typeof window[name] === "function") {
return;
}
window[name] = handler;
};
defineGlobal("giaMitigationShowTab", function(pid, tabName) {
const names = ["plan_board", "corrections", "engage", "fundamentals", "ask_ai"];
names.forEach(function(name) {
const pane = document.getElementById("mitigation-tab-" + pid + "-" + name);
@@ -855,11 +861,9 @@
shell.querySelectorAll('input[name="active_tab"]').forEach(function(input) {
input.value = tabName;
});
};
}
});
if (typeof window.giaMitigationToggleEdit !== "function") {
window.giaMitigationToggleEdit = function(button) {
defineGlobal("giaMitigationToggleEdit", function(button) {
const form = button ? button.closest("form") : null;
if (!form) {
return;
@@ -885,31 +889,25 @@
} else {
form.requestSubmit();
}
};
}
});
if (typeof window.giaEngageSetAction !== "function") {
window.giaEngageSetAction = function(pid, action) {
defineGlobal("giaEngageSetAction", function(pid, action) {
const actionInput = document.getElementById("engage-action-input-" + pid);
if (actionInput) {
actionInput.value = action;
}
};
}
});
if (typeof window.giaEngageAutoPreview !== "function") {
window.giaEngageAutoPreview = function(pid) {
defineGlobal("giaEngageAutoPreview", function(pid) {
const form = document.getElementById("engage-form-" + pid);
if (!form) {
return;
}
window.giaEngageSetAction(pid, "preview");
form.requestSubmit();
};
}
});
if (typeof window.giaEngageSelect !== "function") {
window.giaEngageSelect = function(pid, kind, value, node) {
defineGlobal("giaEngageSelect", function(pid, kind, value, node) {
let inputId = "";
if (kind === "share") {
inputId = "engage-share-input-" + pid;
@@ -928,8 +926,7 @@
li.classList.add("is-active");
}
window.giaEngageAutoPreview(pid);
};
}
});
window.giaWorkspaceOpenTab(personId, "plan_board", false);
syncTargetInputs();

View File

@@ -1650,24 +1650,7 @@
button.classList.add("is-loading");
setStatus("Requesting history sync…", "info");
try {
const payload = new URLSearchParams();
payload.set("service", thread.dataset.service || "");
payload.set("identifier", thread.dataset.identifier || "");
if (thread.dataset.person) {
payload.set("person", thread.dataset.person);
}
payload.set("limit", thread.dataset.limit || "60");
const response = await fetch(historySyncUrl, {
method: "POST",
credentials: "same-origin",
headers: {
"X-CSRFToken": csrfToken,
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json"
},
body: payload.toString(),
});
const result = await response.json();
const result = await postFormJson(historySyncUrl, queryParams());
if (!result.ok) {
setStatus(
String(result.message || result.error || "History sync failed."),
@@ -2352,14 +2335,7 @@
}
panelState.polling = true;
try {
const params = new URLSearchParams();
params.set("service", thread.dataset.service || "");
params.set("identifier", thread.dataset.identifier || "");
if (thread.dataset.person) {
params.set("person", thread.dataset.person);
}
params.set("limit", thread.dataset.limit || "60");
params.set("after_ts", String(lastTs));
const params = queryParams({ after_ts: String(lastTs) });
const response = await fetch(thread.dataset.pollUrl + "?" + params.toString(), {
method: "GET",
credentials: "same-origin",
@@ -2532,7 +2508,7 @@
return active;
};
const queryParams = function () {
const queryParams = function (extraParams) {
const params = new URLSearchParams();
params.set("service", thread.dataset.service || "");
params.set("identifier", thread.dataset.identifier || "");
@@ -2540,9 +2516,47 @@
params.set("person", thread.dataset.person);
}
params.set("limit", thread.dataset.limit || "60");
const extras =
extraParams && typeof extraParams === "object" ? extraParams : {};
Object.keys(extras).forEach(function (key) {
const value = extras[key];
if (value === undefined || value === null || value === "") {
return;
}
params.set(String(key), String(value));
});
return params;
};
const postFormJson = async function (url, params) {
const response = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"X-CSRFToken": csrfToken,
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json"
},
body: params.toString(),
});
if (!response.ok) {
throw new Error("Request failed");
}
return response.json();
};
const getJson = async function (url) {
const response = await fetch(url, {
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
});
if (!response.ok) {
throw new Error("Request failed");
}
return response.json();
};
const titleCase = function (value) {
const raw = String(value || "").trim().toLowerCase();
if (!raw) {
@@ -2625,6 +2639,17 @@
}
};
const cardContentNode = function (card) {
return card ? card.querySelector(".compose-ai-content") : null;
};
const setCardMessage = function (card, message) {
const node = cardContentNode(card);
if (node) {
node.textContent = String(message || "");
}
};
const openEngage = function (sourceRef) {
const engageCard = showCard("engage");
if (!engageCard) {
@@ -2644,19 +2669,19 @@
}
setCardLoading(card, true);
try {
const response = await fetch(thread.dataset.draftsUrl + "?" + queryParams().toString(), {
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
});
const payload = await response.json();
const payload = await getJson(
thread.dataset.draftsUrl + "?" + queryParams().toString()
);
setCardLoading(card, false);
if (!payload.ok) {
card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load drafts.";
setCardMessage(card, payload.error || "Failed to load drafts.");
return;
}
const drafts = Array.isArray(payload.drafts) ? payload.drafts : [];
const container = card.querySelector(".compose-ai-content");
const container = cardContentNode(card);
if (!container) {
return;
}
container.innerHTML = "";
const engageButton = document.createElement("button");
engageButton.type = "button";
@@ -2695,7 +2720,7 @@
});
} catch (err) {
setCardLoading(card, false);
card.querySelector(".compose-ai-content").textContent = "Failed to load drafts.";
setCardMessage(card, "Failed to load drafts.");
}
};
@@ -2706,21 +2731,18 @@
}
setCardLoading(card, true);
try {
const response = await fetch(thread.dataset.summaryUrl + "?" + queryParams().toString(), {
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
});
const payload = await response.json();
const payload = await getJson(
thread.dataset.summaryUrl + "?" + queryParams().toString()
);
setCardLoading(card, false);
if (!payload.ok) {
card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load summary.";
setCardMessage(card, payload.error || "Failed to load summary.");
return;
}
card.querySelector(".compose-ai-content").textContent = String(payload.summary || "");
setCardMessage(card, String(payload.summary || ""));
} catch (err) {
setCardLoading(card, false);
card.querySelector(".compose-ai-content").textContent = "Failed to load summary.";
setCardMessage(card, "Failed to load summary.");
}
};
@@ -2731,17 +2753,14 @@
}
setCardLoading(card, true);
try {
const response = await fetch(
thread.dataset.quickInsightsUrl + "?" + queryParams().toString(),
{
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
}
const payload = await getJson(
thread.dataset.quickInsightsUrl + "?" + queryParams().toString()
);
const payload = await response.json();
setCardLoading(card, false);
const container = card.querySelector(".compose-ai-content");
const container = cardContentNode(card);
if (!container) {
return;
}
if (!payload.ok) {
container.textContent = payload.error || "Failed to load quick insights.";
return;
@@ -2994,8 +3013,7 @@
}
} catch (err) {
setCardLoading(card, false);
card.querySelector(".compose-ai-content").textContent =
"Failed to load quick insights.";
setCardMessage(card, "Failed to load quick insights.");
}
};
@@ -3035,18 +3053,12 @@
if (showCustom && customValue) {
params.set("custom_text", customValue);
}
const response = await fetch(
thread.dataset.engagePreviewUrl + "?" + params.toString(),
{
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
}
const payload = await getJson(
thread.dataset.engagePreviewUrl + "?" + params.toString()
);
const payload = await response.json();
setCardLoading(card, false);
if (!payload.ok) {
card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load engage preview.";
setCardMessage(card, payload.error || "Failed to load engage preview.");
panelState.engageToken = "";
return;
}
@@ -3081,11 +3093,11 @@
if (payload.artifact) {
text = text + "\n\nSource: " + String(payload.artifact);
}
card.querySelector(".compose-ai-content").textContent = text;
setCardMessage(card, text);
sendBtn.disabled = !(confirm.checked && panelState.engageToken);
} catch (err) {
setCardLoading(card, false);
card.querySelector(".compose-ai-content").textContent = "Failed to load engage preview.";
setCardMessage(card, "Failed to load engage preview.");
panelState.engageToken = "";
} finally {
if (refreshBtn) {
@@ -3141,27 +3153,13 @@
if (!panelState.engageToken) {
return;
}
const formData = new URLSearchParams();
formData.set("service", thread.dataset.service || "");
formData.set("identifier", thread.dataset.identifier || "");
if (thread.dataset.person) {
formData.set("person", thread.dataset.person);
}
formData.set("engage_token", panelState.engageToken);
formData.set("failsafe_arm", confirm.checked ? "1" : "0");
formData.set("failsafe_confirm", confirm.checked ? "1" : "0");
try {
const response = await fetch(thread.dataset.engageSendUrl, {
method: "POST",
credentials: "same-origin",
headers: {
"X-CSRFToken": csrfToken,
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json"
},
body: formData.toString()
const formData = queryParams({
engage_token: panelState.engageToken,
failsafe_arm: confirm.checked ? "1" : "0",
failsafe_confirm: confirm.checked ? "1" : "0",
});
const payload = await response.json();
try {
const payload = await postFormJson(thread.dataset.engageSendUrl, formData);
if (!payload.ok) {
flashCompose("is-send-fail");
setStatus(payload.error || "Engage send failed.", "danger");
@@ -3352,28 +3350,26 @@
// Cancel send support: show a cancel button while the form request is pending.
let cancelBtn = null;
const cancelSendRequest = function (commandId) {
return postFormJson(
'{% url "compose_cancel_send" %}',
queryParams({ command_id: String(commandId || "") })
);
};
const showCancelButton = function () {
if (cancelBtn) return;
cancelBtn = document.createElement('button');
cancelBtn.type = 'button';
cancelBtn.className = 'button is-danger is-light is-small compose-cancel-send-btn';
cancelBtn.textContent = 'Cancel Send';
cancelBtn.addEventListener('click', function () {
// Post cancel by service+identifier
const payload = new URLSearchParams();
payload.set('service', thread.dataset.service || '');
payload.set('identifier', thread.dataset.identifier || '');
fetch('{% url "compose_cancel_send" %}', {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-CSRFToken': '{{ csrf_token }}', 'Content-Type': 'application/x-www-form-urlencoded' },
body: payload.toString(),
}).then(function (resp) {
// Hide cancel once requested
cancelBtn.addEventListener('click', async function () {
try {
await cancelSendRequest("");
} catch (e) {
// Ignore cancel failures.
} finally {
hideCancelButton();
}).catch(function () {
hideCancelButton();
});
}
});
if (statusBox) {
statusBox.appendChild(cancelBtn);
@@ -3498,24 +3494,16 @@
btn.type = 'button';
btn.className = 'button is-danger is-light is-small compose-persistent-cancel-btn';
btn.textContent = 'Cancel Queued Send';
btn.addEventListener('click', function () {
const payload = new URLSearchParams();
payload.set('service', thread.dataset.service || '');
payload.set('identifier', thread.dataset.identifier || '');
payload.set('command_id', String(commandId || ''));
fetch('{% url "compose_cancel_send" %}', {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-CSRFToken': '{{ csrf_token }}', 'Content-Type': 'application/x-www-form-urlencoded' },
body: payload.toString(),
}).then(function (resp) {
btn.addEventListener('click', async function () {
try {
await cancelSendRequest(String(commandId || ''));
stopPendingCommandPolling();
hidePersistentCancelButton();
setStatus('Send cancelled.', 'warning');
poll(true);
}).catch(function () {
await poll(true);
} catch (e) {
hidePersistentCancelButton();
});
}
});
container.appendChild(btn);
if (statusBox) {

View File

@@ -108,6 +108,17 @@ def _safe_after_ts(raw) -> int:
return max(0, value)
def _request_scope(request, source: str = "GET"):
data = request.GET if str(source).upper() == "GET" else request.POST
service = _default_service(data.get("service"))
identifier = str(data.get("identifier") or "").strip()
person = None
person_id = data.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
return service, identifier, person
def _format_ts_label(ts_value: int) -> str:
try:
as_dt = datetime.fromtimestamp(int(ts_value) / 1000, tz=dt_timezone.utc)
@@ -2111,12 +2122,7 @@ class ComposeWorkspace(LoginRequiredMixin, View):
template_name = "pages/compose-workspace.html"
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = Person.objects.filter(id=person_id, user=request.user).first()
service, identifier, person = _request_scope(request, "GET")
limit = _safe_limit(request.GET.get("limit") or 40)
initial_widget_url = ""
@@ -2307,12 +2313,7 @@ class ComposePage(LoginRequiredMixin, View):
template_name = "pages/compose.html"
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return HttpResponseBadRequest("Missing contact identifier.")
@@ -2328,12 +2329,7 @@ class ComposePage(LoginRequiredMixin, View):
class ComposeWidget(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return HttpResponseBadRequest("Missing contact identifier.")
@@ -2361,12 +2357,7 @@ class ComposeWidget(LoginRequiredMixin, View):
class ComposeThread(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return HttpResponseBadRequest("Missing contact identifier.")
@@ -2565,12 +2556,7 @@ class ComposeHistorySync(LoginRequiredMixin, View):
return len(duplicate_ids)
def post(self, request):
service = _default_service(request.POST.get("service"))
identifier = str(request.POST.get("identifier") or "").strip()
person = None
person_id = request.POST.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "POST")
if not identifier and person is None:
return JsonResponse(
{
@@ -2737,8 +2723,7 @@ class ComposeHistorySync(LoginRequiredMixin, View):
class ComposeCancelSend(LoginRequiredMixin, View):
def post(self, request):
service = _default_service(request.POST.get("service"))
identifier = str(request.POST.get("identifier") or "").strip()
service, identifier, _ = _request_scope(request, "POST")
command_id = str(request.POST.get("command_id") or "").strip()
if not identifier:
return JsonResponse({"ok": False, "error": "missing_identifier"})
@@ -2842,12 +2827,7 @@ class ComposeMediaBlob(LoginRequiredMixin, View):
class ComposeDrafts(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return JsonResponse({"ok": False, "error": "Missing contact identifier."})
@@ -2908,12 +2888,7 @@ class ComposeDrafts(LoginRequiredMixin, View):
class ComposeSummary(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return JsonResponse({"ok": False, "error": "Missing contact identifier."})
@@ -2976,12 +2951,7 @@ class ComposeSummary(LoginRequiredMixin, View):
class ComposeQuickInsights(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return JsonResponse({"ok": False, "error": "Missing contact identifier."})
@@ -3097,12 +3067,7 @@ class ComposeQuickInsights(LoginRequiredMixin, View):
class ComposeEngagePreview(LoginRequiredMixin, View):
def get(self, request):
service = _default_service(request.GET.get("service"))
identifier = str(request.GET.get("identifier") or "").strip()
person = None
person_id = request.GET.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "GET")
if not identifier and person is None:
return JsonResponse({"ok": False, "error": "Missing contact identifier."})
@@ -3228,12 +3193,7 @@ class ComposeEngagePreview(LoginRequiredMixin, View):
class ComposeEngageSend(LoginRequiredMixin, View):
def post(self, request):
service = _default_service(request.POST.get("service"))
identifier = str(request.POST.get("identifier") or "").strip()
person = None
person_id = request.POST.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "POST")
if not identifier and person is None:
return JsonResponse({"ok": False, "error": "Missing contact identifier."})
@@ -3319,12 +3279,7 @@ class ComposeSend(LoginRequiredMixin, View):
return response
def post(self, request):
service = _default_service(request.POST.get("service"))
identifier = str(request.POST.get("identifier") or "").strip()
person = None
person_id = request.POST.get("person")
if person_id:
person = get_object_or_404(Person, id=person_id, user=request.user)
service, identifier, person = _request_scope(request, "POST")
render_mode = str(request.POST.get("render_mode") or "page").strip().lower()
if render_mode not in {"page", "widget"}:
render_mode = "page"

File diff suppressed because it is too large Load Diff