Skip to content

Commit 780bcc4

Browse files
authored
Merge pull request #247 from bmlt-enabled/feature/fixing-tz-issue
Feature/fixing tz issue
2 parents 96f7f5c + c00ace9 commit 780bcc4

13 files changed

Lines changed: 481 additions & 27 deletions

assets/js/src/components/admin/AnnouncementEditor.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { __ } from '@wordpress/i18n';
1414
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
1515
import { apiFetch } from '../../util';
1616
import { useEventProvider } from '../providers/EventProvider';
17+
import { getTimezoneOptions, getUserTimezone } from '../../timezones';
1718

1819
// Event Search Modal Component with infinite scroll
1920
const EventSearchModal = ({ isOpen, onClose, onSelectEvent, onRemoveEvent, linkedEventRefs, getRefKey }) => {
@@ -575,6 +576,17 @@ const AnnouncementEditor = () => {
575576
fetchSubscriptionSettings();
576577
}, [postType]);
577578

579+
// Determine if this is a new announcement
580+
const isNewAnnouncement = postStatus === 'auto-draft';
581+
582+
// Auto-set timezone for new announcements based on browser timezone
583+
useEffect(() => {
584+
if (isNewAnnouncement && !meta.display_timezone) {
585+
const detectedTimezone = getUserTimezone();
586+
editPost({ meta: { ...meta, display_timezone: detectedTimezone } });
587+
}
588+
}, [isNewAnnouncement, meta.display_timezone]);
589+
578590
if (postType !== 'mayo_announcement') return null;
579591

580592
// Filter service bodies by subscription settings
@@ -898,6 +910,19 @@ const AnnouncementEditor = () => {
898910
Leave empty to show indefinitely
899911
</p>
900912
</div>
913+
<div style={{ marginTop: '12px' }}>
914+
<SelectControl
915+
label="Timezone"
916+
value={meta.display_timezone || ''}
917+
options={[
918+
{ label: '-- No timezone set --', value: '' },
919+
...getTimezoneOptions()
920+
]}
921+
onChange={value => updateMetaValue('display_timezone', value)}
922+
__nextHasNoMarginBottom={true}
923+
__next40pxDefaultSize={true}
924+
/>
925+
</div>
901926
</PanelBody>
902927

903928
<PanelBody title="Priority" initialOpen={true}>

assets/js/src/components/admin/EventBlockEditorSidebar.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ const EventBlockEditorSidebar = () => {
7070
editPost({ meta: { ...meta, [key]: value } });
7171
};
7272

73+
// Auto-set timezone for new events based on browser timezone
74+
useEffect(() => {
75+
if (isNewEvent && !meta.timezone) {
76+
const detectedTimezone = getUserTimezone();
77+
updateMetaValue('timezone', detectedTimezone);
78+
}
79+
}, [isNewEvent, meta.timezone]);
80+
7381
const recurringPattern = meta.recurring_pattern || {
7482
type: 'none',
7583
interval: 1,
@@ -219,7 +227,7 @@ const EventBlockEditorSidebar = () => {
219227
</div>
220228
<SelectControl
221229
label="Timezone"
222-
value={meta.timezone || (isNewEvent ? getUserTimezone() : '')}
230+
value={meta.timezone || ''}
223231
options={[
224232
{ label: '-- No timezone set --', value: '' },
225233
...getTimezoneOptions()

assets/js/src/components/public/EventAnnouncement.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import AnnouncementBanner from './AnnouncementBanner';
33
import AnnouncementModal from './AnnouncementModal';
44
import AnnouncementBellIcon from './AnnouncementBellIcon';
55
import { apiFetch } from '../../util';
6+
import { getUserTimezone } from '../../timezones';
67

78
const EventAnnouncement = ({ settings = {} }) => {
89
const [announcements, setAnnouncements] = useState([]);
@@ -40,6 +41,9 @@ const EventAnnouncement = ({ settings = {} }) => {
4041
return Date.now() - timestamp < twentyFourHours;
4142
}, [getDismissalKey]);
4243

44+
// Get user's timezone for accurate time filtering
45+
const userTimezone = getUserTimezone();
46+
4347
// Fetch announcements from the new announcements API
4448
useEffect(() => {
4549
const fetchAnnouncements = async () => {
@@ -48,6 +52,10 @@ const EventAnnouncement = ({ settings = {} }) => {
4852
// Use the new announcements endpoint which handles date filtering server-side
4953
let endpoint = '/announcements?per_page=20';
5054

55+
// Add timezone and current time for accurate end time filtering
56+
endpoint += `&timezone=${encodeURIComponent(userTimezone)}`;
57+
endpoint += `&current_time=${encodeURIComponent(new Date().toISOString())}`;
58+
5159
if (categories) {
5260
endpoint += `&categories=${encodeURIComponent(categories)}`;
5361
}
@@ -87,7 +95,7 @@ const EventAnnouncement = ({ settings = {} }) => {
8795
};
8896

8997
fetchAnnouncements();
90-
}, [categories, categoryRelation, tags, priority, orderBy, order, checkDismissed]);
98+
}, [categories, categoryRelation, tags, priority, orderBy, order, userTimezone, checkDismissed]);
9199

92100
// Handle dismiss
93101
const handleDismiss = useCallback(() => {

assets/js/src/components/public/EventArchive.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import { useState, useEffect } from '@wordpress/element';
22
import { formatTimezone, apiFetch } from '../../util'; // Import the helper function
33
import { useEventProvider } from '../providers/EventProvider';
4+
import { getUserTimezone } from '../../timezones';
45

56
const EventArchive = () => {
67
const [events, setEvents] = useState([]);
78
const [loading, setLoading] = useState(true);
89
const [error, setError] = useState(null);
910
const { getServiceBodyName } = useEventProvider();
1011

12+
// Get user's current timezone
13+
const userTimezone = getUserTimezone();
14+
1115
useEffect(() => {
1216
const fetchEvents = async () => {
1317
try {
14-
const response = await apiFetch('/events?archive=true');
18+
const endpoint = `/events?archive=true`
19+
+ `&timezone=${encodeURIComponent(userTimezone)}`
20+
+ `&current_time=${encodeURIComponent(new Date().toISOString())}`;
21+
const response = await apiFetch(endpoint);
1522

1623
// Ensure we have a valid response and it's an array
1724
if (response && Array.isArray(response)) {

assets/js/src/components/public/EventList.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ const EventList = ({ widget = false, settings = {} }) => {
288288
+ `&page=${page}`
289289
+ `&per_page=${perPage}`
290290
+ `&timezone=${encodeURIComponent(userTimezone)}`
291+
+ `&current_time=${encodeURIComponent(new Date().toISOString())}`
291292
+ `&archive=${archive}`
292293
+ `&order=${order}`;
293294

@@ -368,6 +369,7 @@ const EventList = ({ widget = false, settings = {} }) => {
368369
+ `&tags=${tags}`
369370
+ `&source_ids=${sourceIds}`
370371
+ `&timezone=${encodeURIComponent(userTimezone)}`
372+
+ `&current_time=${encodeURIComponent(new Date().toISOString())}`
371373
+ `&order=${order}`
372374
+ `&start_date=${startDate}`
373375
+ `&end_date=${endDate}`

assets/js/src/components/public/EventModal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ const EventModal = ({ event, timeFormat, onClose }) => {
111111
</div>
112112
)}
113113

114-
{event.source_id && event.source_id !== 'local' && (
114+
{event.source && event.source.type === 'external' && (
115115
<div className="mayo-event-modal-source">
116116
<span className="dashicons dashicons-admin-site"></span>
117-
<span>External Event</span>
117+
<span>{event.source.name}</span>
118118
</div>
119119
)}
120120
</div>

assets/js/src/components/public/cards/EventCard.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ const EventCard = ({ event, timeFormat, forceExpanded }) => {
108108
{formatDateTimeDisplay(event, timeFormat)}
109109
</span>
110110
)}
111-
{event.source_id && event.source_id !== 'local' && (
112-
<span className="mayo-event-source">
113-
External Event
111+
{event.source && event.source.type === 'external' && (
112+
<span className="mayo-event-source" title={`From ${event.source.name}`}>
113+
{event.source.name}
114114
</span>
115115
)}
116116
{event.meta.service_body && (

includes/Announcement.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ public static function register_meta_fields() {
102102
}
103103
]);
104104

105+
register_post_meta('mayo_announcement', 'display_timezone', [
106+
'show_in_rest' => true,
107+
'single' => true,
108+
'type' => 'string',
109+
'default' => '',
110+
'sanitize_callback' => 'sanitize_text_field',
111+
'auth_callback' => function() {
112+
return current_user_can('edit_posts');
113+
}
114+
]);
115+
105116
register_post_meta('mayo_announcement', 'priority', [
106117
'show_in_rest' => true,
107118
'single' => true,

includes/Rest/AnnouncementsController.php

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,19 @@ public static function get_announcements($request) {
6464
$active_only = !isset($params['active']) || $params['active'] !== 'false';
6565
$orderby = isset($params['orderby']) ? sanitize_text_field($params['orderby']) : 'date';
6666
$order = isset($params['order']) ? strtoupper(sanitize_text_field($params['order'])) : '';
67+
$timezone = isset($params['timezone']) ? urldecode(sanitize_text_field(wp_unslash($params['timezone']))) : wp_timezone_string();
68+
$current_time = isset($params['current_time']) ? sanitize_text_field(wp_unslash($params['current_time'])) : null;
6769

6870
$today = current_time('Y-m-d');
6971

72+
// Create DateTime object for current time with proper timezone
73+
if ($current_time) {
74+
$now = new \DateTime($current_time);
75+
$now->setTimezone(new \DateTimeZone($timezone));
76+
} else {
77+
$now = new \DateTime('now', new \DateTimeZone($timezone));
78+
}
79+
7080
$args = [
7181
'post_type' => 'mayo_announcement',
7282
'post_status' => 'publish',
@@ -144,9 +154,46 @@ public static function get_announcements($request) {
144154

145155
$posts = get_posts($args);
146156

157+
// Post-filter based on end time if active_only is enabled
158+
// The meta_query only checks dates, so we need to filter by time here
159+
if ($active_only) {
160+
$posts = array_filter($posts, function($post) use ($now, $timezone) {
161+
$end_date_str = get_post_meta($post->ID, 'display_end_date', true);
162+
163+
// If no end date, the announcement is considered active
164+
if (empty($end_date_str)) {
165+
return true;
166+
}
167+
168+
$end_time_str = get_post_meta($post->ID, 'display_end_time', true);
169+
170+
// Use the announcement's own timezone if set, otherwise fall back to request timezone
171+
$announcement_timezone = get_post_meta($post->ID, 'display_timezone', true);
172+
$effective_timezone = !empty($announcement_timezone) ? $announcement_timezone : $timezone;
173+
174+
// Create end DateTime with proper time and timezone
175+
$tz = new \DateTimeZone($effective_timezone);
176+
$end_datetime = new \DateTime($end_date_str, $tz);
177+
if (!empty($end_time_str)) {
178+
$time_parts = explode(':', $end_time_str);
179+
$end_datetime->setTime(
180+
(int)($time_parts[0] ?? 23),
181+
(int)($time_parts[1] ?? 59),
182+
(int)($time_parts[2] ?? 59)
183+
);
184+
} else {
185+
// If no end time specified, use end of day
186+
$end_datetime->setTime(23, 59, 59);
187+
}
188+
189+
// Announcement is active if end datetime is in the future
190+
return $end_datetime >= $now;
191+
});
192+
}
193+
147194
$announcements = [];
148195
foreach ($posts as $post) {
149-
$announcements[] = self::format_announcement($post);
196+
$announcements[] = self::format_announcement($post, $now);
150197
}
151198

152199
// Sort announcements
@@ -377,9 +424,10 @@ private static function send_submission_email($post_id, $params) {
377424
* Format announcement data for API response
378425
*
379426
* @param \WP_Post $post
427+
* @param \DateTime|null $now Current DateTime for is_active calculation
380428
* @return array
381429
*/
382-
public static function format_announcement($post) {
430+
public static function format_announcement($post, $now = null) {
383431
$linked_refs = Announcement::get_linked_event_refs($post->ID);
384432
$linked_event_data = [];
385433

@@ -423,17 +471,57 @@ public static function format_announcement($post) {
423471
}
424472
}
425473

426-
// Calculate is_active based on display dates
427-
$today = current_time('Y-m-d');
474+
// Calculate is_active based on display dates and times
428475
$display_start_date = get_post_meta($post->ID, 'display_start_date', true);
476+
$display_start_time = get_post_meta($post->ID, 'display_start_time', true);
429477
$display_end_date = get_post_meta($post->ID, 'display_end_date', true);
478+
$display_end_time = get_post_meta($post->ID, 'display_end_time', true);
479+
$display_timezone = get_post_meta($post->ID, 'display_timezone', true);
480+
481+
// Use provided $now or fall back to current time
482+
if (!$now) {
483+
$now = new \DateTime('now', new \DateTimeZone(wp_timezone_string()));
484+
}
485+
486+
// Use the announcement's own timezone if set, otherwise get timezone from $now
487+
$tz = !empty($display_timezone) ? new \DateTimeZone($display_timezone) : $now->getTimezone();
430488

431489
$is_active = true;
432-
if ($display_start_date && $display_start_date > $today) {
433-
$is_active = false;
490+
491+
// Check start date/time
492+
if ($display_start_date) {
493+
$start_datetime = new \DateTime($display_start_date, $tz);
494+
if (!empty($display_start_time)) {
495+
$time_parts = explode(':', $display_start_time);
496+
$start_datetime->setTime(
497+
(int)($time_parts[0] ?? 0),
498+
(int)($time_parts[1] ?? 0),
499+
(int)($time_parts[2] ?? 0)
500+
);
501+
} else {
502+
$start_datetime->setTime(0, 0, 0);
503+
}
504+
if ($start_datetime > $now) {
505+
$is_active = false;
506+
}
434507
}
435-
if ($display_end_date && $display_end_date < $today) {
436-
$is_active = false;
508+
509+
// Check end date/time
510+
if ($display_end_date && $is_active) {
511+
$end_datetime = new \DateTime($display_end_date, $tz);
512+
if (!empty($display_end_time)) {
513+
$time_parts = explode(':', $display_end_time);
514+
$end_datetime->setTime(
515+
(int)($time_parts[0] ?? 23),
516+
(int)($time_parts[1] ?? 59),
517+
(int)($time_parts[2] ?? 59)
518+
);
519+
} else {
520+
$end_datetime->setTime(23, 59, 59);
521+
}
522+
if ($end_datetime < $now) {
523+
$is_active = false;
524+
}
437525
}
438526

439527
$permalink = get_permalink($post->ID);
@@ -453,6 +541,7 @@ public static function format_announcement($post) {
453541
'display_start_time' => get_post_meta($post->ID, 'display_start_time', true),
454542
'display_end_date' => $display_end_date,
455543
'display_end_time' => get_post_meta($post->ID, 'display_end_time', true),
544+
'display_timezone' => $display_timezone,
456545
'priority' => get_post_meta($post->ID, 'priority', true) ?: 'normal',
457546
'service_body' => get_post_meta($post->ID, 'service_body', true) ?: '',
458547
'is_active' => $is_active,

0 commit comments

Comments
 (0)