chore: bump version to 1.4.5

This commit is contained in:
Linus Rath
2026-03-20 18:10:00 +01:00
parent 616e4d018d
commit d68b81e6b8
6 changed files with 35 additions and 39 deletions
+25
View File
@@ -1,5 +1,30 @@
# Changelog
## 1.4.5 (2026-03-20)
### Features
- **Calendar**: Add prev/next navigation buttons and date label to desktop calendar toolbar
- **Calendar**: Add pending event preview functionality to calendar views and event modal
- **Calendar**: Add setting to show event start time in month view
- **Contacts**: Implement pagination for fetching contacts with maxObjectsInGet capability
- **Email**: Add attachment position setting in email settings
- **Layout**: Add mobile visibility toggle for sidebar apps
- **Error**: Add NotFound component to handle 404 errors and redirect unauthenticated users
### Fixes
- **Auth**: Enhance account switching logic and clear stores on account change
- **Auth**: Improve account restoration logic and handle stale accounts
- **Auth**: Improve draft handling in email composer and enhance session cookie verification
- **Calendar**: Expand recurring events in CalendarEvent/query so individual occurrences are returned (#65)
- **Calendar**: Validate event start field when fetching calendar events
- **Calendar**: Auto-scroll agenda view to today's events and include today's date in groups
- **Calendar**: Correct JSX syntax in CalendarToolbar component
- **Dependencies**: Update flatted to 3.4.2
- **DevOps**: Use native ARM runners instead of QEMU for Docker builds
- **DevOps**: Enhance health check with detailed memory diagnostics and stable liveness probe
## 1.4.4 (2026-03-19)
### Features
+1 -1
View File
@@ -1 +1 @@
1.4.4
1.4.5
+6 -24
View File
@@ -70,12 +70,9 @@ export default function Home() {
const [isLoadingConversation, setIsLoadingConversation] = useState(false);
const [previewAttachment, setPreviewAttachment] = useState<{ blobId: string; name: string; type?: string } | null>(null);
const markAsReadTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const { isAuthenticated, client, logout, checkAuth, isLoading: authLoading, connectionLost, activeAccountId } = useAuthStore();
const { isAuthenticated, client, logout, checkAuth, isLoading: authLoading, connectionLost } = useAuthStore();
const { identities } = useIdentityStore();
// Track account switches to force data reload
const lastLoadedAccountRef = useRef<string | null>(null);
// Mobile/tablet responsive hooks
const { isMobile, isTablet } = useDeviceDetection();
const { activeView, sidebarOpen, setSidebarOpen, setActiveView, tabletListVisible, setTabletListVisible, sidebarWidth, emailListWidth, setSidebarWidth, setEmailListWidth, persistColumnWidths, sidebarCollapsed, resetSidebarWidth, resetEmailListWidth } = useUIStore();
@@ -292,19 +289,9 @@ export default function Home() {
}
}, [initialCheckDone, isAuthenticated, authLoading, router]);
// Load mailboxes and emails when authenticated
// Re-fetches on initial load (mailboxes empty) or when account switches
// Load mailboxes and emails when authenticated (only if not already loaded)
useEffect(() => {
const accountChanged = lastLoadedAccountRef.current !== null && lastLoadedAccountRef.current !== activeAccountId;
if (isAuthenticated && client && (mailboxes.length === 0 || accountChanged)) {
lastLoadedAccountRef.current = activeAccountId;
// Clear stale selected email from the previous account so the viewer
// doesn't flash old content while fresh data loads.
if (accountChanged) {
selectEmail(null);
}
if (isAuthenticated && client && mailboxes.length === 0) {
const loadData = async () => {
try {
// First fetch mailboxes and quota (inbox will be auto-selected in fetchMailboxes)
@@ -350,18 +337,15 @@ export default function Home() {
}
};
loadData();
} else if (isAuthenticated && client && lastLoadedAccountRef.current === null) {
// First render with existing data (e.g. restored from snapshot) — just record the account
lastLoadedAccountRef.current = activeAccountId;
}
// Cleanup push notifications on unmount or client change
// Cleanup push notifications on unmount
return () => {
if (client) {
client.closePushNotifications();
}
};
}, [isAuthenticated, client, mailboxes.length, activeAccountId, fetchMailboxes, fetchEmails, fetchQuota, fetchTagCounts, handleStateChange, setPushConnected, selectEmail]);
}, [isAuthenticated, client, mailboxes.length, fetchMailboxes, fetchEmails, fetchQuota, fetchTagCounts, handleStateChange, setPushConnected]);
// Auto-fetch full email content when an email is auto-selected (e.g. after delete/archive)
useEffect(() => {
@@ -487,9 +471,7 @@ export default function Home() {
};
const handleEditDraft = (email?: Email) => {
// Guard: when used directly as an onClick handler, the click event is passed
// as the first argument. Detect this and fall back to selectedEmail.
const draft = (email && 'mailboxIds' in email) ? email : selectedEmail;
const draft = email || selectedEmail;
if (!draft) return;
const bodyText = draft.bodyValues
? Object.values(draft.bodyValues).map(v => v.value).join('\n')
+2 -5
View File
@@ -137,8 +137,7 @@
"show_all": "All",
"no_icons_found": "No icons found",
"inline_badge": "Inline",
"tab_badge": "Tab",
"show_on_mobile": "Show on Mobile"
"tab_badge": "Tab"
},
"email_list": {
"no_emails": "No messages found",
@@ -1827,9 +1826,7 @@
"notification_sound": "Notification sound",
"notification_sound_desc": "Play a sound for calendar alerts",
"invitation_parsing": "Parse email invitations",
"invitation_parsing_desc": "Detect calendar invitations in email attachments and show calendar actions",
"show_time_in_month_view": "Show start time in month view",
"show_time_in_month_view_desc": "Display the event start time alongside the title in month view"
"invitation_parsing_desc": "Detect calendar invitations in email attachments and show calendar actions"
},
"days": {
"monday": "Monday",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bulwark-webmail",
"version": "1.4.4",
"version": "1.4.5",
"description": "Bulwark Webmail — a modern webmail client built for Stalwart Mail Server",
"author": "Bulwark Webmail <bulwark@rbm.systems>",
"license": "AGPL-3.0-only",
-8
View File
@@ -45,7 +45,6 @@ export interface SidebarApp {
url: string;
icon: string; // Lucide icon name (e.g. 'Globe', 'Rss')
openMode: 'tab' | 'inline'; // Open in new tab or embed inline
showOnMobile?: boolean; // Show in mobile bottom nav (default false)
}
// Available color palette for keywords
@@ -107,9 +106,6 @@ interface SettingsState {
sessionTimeout: number; // minutes (0 = never)
trustedSenders: string[]; // Email addresses that can load external content
// Calendar
showTimeInMonthView: boolean;
// Calendar Notifications
calendarNotificationsEnabled: boolean;
calendarNotificationSound: boolean;
@@ -205,9 +201,6 @@ const DEFAULT_SETTINGS = {
sessionTimeout: 0, // Never
trustedSenders: [] as string[],
// Calendar
showTimeInMonthView: false,
// Calendar Notifications
calendarNotificationsEnabled: true,
calendarNotificationSound: true,
@@ -288,7 +281,6 @@ export const useSettingsStore = create<SettingsState>()(
sendConfirmation: state.sendConfirmation,
defaultReplyMode: state.defaultReplyMode,
sessionTimeout: state.sessionTimeout,
showTimeInMonthView: state.showTimeInMonthView,
calendarNotificationsEnabled: state.calendarNotificationsEnabled,
calendarNotificationSound: state.calendarNotificationSound,
calendarInvitationParsingEnabled: state.calendarInvitationParsingEnabled,