Release v0.1.3

This commit is contained in:
2026-06-26 01:39:19 +02:00
parent 3d82d7b32d
commit e6e243c14d
4 changed files with 43 additions and 8 deletions

View File

@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project]
name = "govoplan-mail"
version = "0.1.2"
version = "0.1.3"
description = "GovOPlaN mail module with backend and WebUI integration."
readme = "README.md"
requires-python = ">=3.12"
license = { file = "LICENSE" }
authors = [{ name = "GovOPlaN" }]
dependencies = [
"govoplan-core>=0.1.2",
"govoplan-core>=0.1.3",
"pydantic>=2,<3",
"redis>=5,<6",
"SQLAlchemy>=2,<3",

View File

@@ -65,7 +65,7 @@ def _mail_router(context: ModuleContext):
manifest = ModuleManifest(
id="mail",
name="Mail",
version="1.0.0",
version="0.1.2",
dependencies=("access",),
optional_dependencies=(),
permissions=PERMISSIONS,
@@ -90,4 +90,3 @@ manifest = ModuleManifest(
def get_manifest() -> ModuleManifest:
return manifest

View File

@@ -1,6 +1,6 @@
{
"name": "@govoplan/mail-webui",
"version": "0.1.1",
"version": "0.1.2",
"private": true,
"type": "module",
"main": "src/index.ts",
@@ -14,7 +14,7 @@
"./styles/mail-profiles.css": "./src/styles/mail-profiles.css"
},
"peerDependencies": {
"@govoplan/core-webui": "^0.1.1",
"@govoplan/core-webui": "^0.1.2",
"lucide-react": "^0.555.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",

View File

@@ -31,7 +31,7 @@ export default function MailboxPage({ settings }: { settings: ApiSettings }) {
const [messages, setMessages] = useState<MailMailboxMessageSummary[]>([]);
const [messageTotalCount, setMessageTotalCount] = useState<number | null>(null);
const [messagePage, setMessagePage] = useState(1);
const [messagePageSize, setMessagePageSize] = useState(50);
const [messagePageSize, setMessagePageSize] = useState(10);
const [messageQuery, setMessageQuery] = useState("");
const [selectedMessage, setSelectedMessage] = useState<MailMailboxMessageDetail | null>(null);
const [selectedMessageKeyState, setSelectedMessageKeyState] = useState("");
@@ -71,6 +71,37 @@ export default function MailboxPage({ settings }: { settings: ApiSettings }) {
useEffect(() => { if (selectedProfileId) void loadFolders(selectedProfileId); }, [selectedProfileId]);
useEffect(() => { if (selectedProfileId && selectedFolder && foldersReady) void loadMessages(selectedProfileId, selectedFolder, messagePage, messagePageSize); }, [foldersReady, messagePage, messagePageSize, selectedProfileId, selectedFolder]);
useEffect(() => {
const handlePreviewShortcut = (event: KeyboardEvent) => {
if (isEditableTarget(event.target)) return;
if (event.key === "Escape") {
if (selectedMessage || selectedMessageKey || pendingMessageKey || detailError) {
event.preventDefault();
setSelectedMessage(null);
setSelectedMessageKeyState("");
selectedMessageKeyRef.current = "";
setPendingMessageKey("");
setDetailError("");
}
return;
}
if (shellBusy || loadingMessage || filteredMessages.length === 0) return;
const selectedIndex = filteredMessages.findIndex((message) => mailboxMessageKey(message.folder || selectedFolder, message.uid) === selectedMessageKey);
let nextIndex: number | null = null;
if (event.key === "ArrowLeft") nextIndex = selectedIndex > 0 ? selectedIndex - 1 : 0;
else if (event.key === "ArrowRight") nextIndex = selectedIndex >= 0 ? Math.min(filteredMessages.length - 1, selectedIndex + 1) : 0;
else if (event.key === "Home") nextIndex = 0;
else if (event.key === "End") nextIndex = filteredMessages.length - 1;
if (nextIndex === null) return;
event.preventDefault();
const nextMessage = filteredMessages[nextIndex];
if (nextMessage) void openMessage(nextMessage);
};
window.addEventListener("keydown", handlePreviewShortcut);
return () => window.removeEventListener("keydown", handlePreviewShortcut);
}, [detailError, filteredMessages, loadingMessage, pendingMessageKey, selectedFolder, selectedMessage, selectedMessageKey, shellBusy]);
useEffect(() => {
const ancestorIds = folderAncestorIds(folderTree, selectedFolder);
if (ancestorIds.length === 0) return;
@@ -458,7 +489,7 @@ function MailboxPagination({ page, pageSize, totalRows, disabled, onPageChange,
const pageCount = Math.max(1, Math.ceil(totalRows / pageSize));
const first = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
const last = Math.min(totalRows, page * pageSize);
const options = [25, 50, 100];
const options = [10, 25, 50, 100];
return (
<div className="data-grid-pagination mailbox-pagination" aria-label="Mailbox message pagination">
<div className="data-grid-pagination-summary">{first}-{last} of {totalRows}</div>
@@ -549,3 +580,8 @@ function formatBytes(value?: number | null): string {
function errorText(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
function isEditableTarget(target: EventTarget | null): boolean {
if (!(target instanceof HTMLElement)) return false;
return target.isContentEditable || ["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName);
}