chore: bump version to 1.4.5
This commit is contained in:
@@ -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
|
||||
|
||||
+6
-24
@@ -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')
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user