diff --git a/public/renderer-config.example b/public/renderer-config.example index a633783..54b7a47 100644 --- a/public/renderer-config.example +++ b/public/renderer-config.example @@ -39,6 +39,7 @@ "room.color.skip.transition": true, "room.landscapes.enabled": true, "room.zoom.enabled": true, + "timezone.settings": "America/Los_Angeles", "avatar.mandatory.libraries": [ "bd:1", "li:0" diff --git a/src/assets/images/hotelview/hotelview-morning.png b/src/assets/images/hotelview/hotelview-morning.png new file mode 100644 index 0000000..62609c2 Binary files /dev/null and b/src/assets/images/hotelview/hotelview-morning.png differ diff --git a/src/assets/images/hotelview/hotelview-night.png b/src/assets/images/hotelview/hotelview-night.png new file mode 100644 index 0000000..70af534 Binary files /dev/null and b/src/assets/images/hotelview/hotelview-night.png differ diff --git a/src/assets/images/hotelview/hotelview-sunset.png b/src/assets/images/hotelview/hotelview-sunset.png new file mode 100644 index 0000000..10122df Binary files /dev/null and b/src/assets/images/hotelview/hotelview-sunset.png differ diff --git a/src/assets/images/hotelview/night/night-infobus.gif b/src/assets/images/hotelview/night/night-infobus.gif new file mode 100644 index 0000000..df2b710 Binary files /dev/null and b/src/assets/images/hotelview/night/night-infobus.gif differ diff --git a/src/assets/images/hotelview/night/night-lobby.png b/src/assets/images/hotelview/night/night-lobby.png new file mode 100644 index 0000000..5a4ac08 Binary files /dev/null and b/src/assets/images/hotelview/night/night-lobby.png differ diff --git a/src/assets/images/hotelview/night/night-park.png b/src/assets/images/hotelview/night/night-park.png new file mode 100644 index 0000000..06f7b63 Binary files /dev/null and b/src/assets/images/hotelview/night/night-park.png differ diff --git a/src/assets/images/hotelview/night/night-picnic.gif b/src/assets/images/hotelview/night/night-picnic.gif new file mode 100644 index 0000000..39cb34b Binary files /dev/null and b/src/assets/images/hotelview/night/night-picnic.gif differ diff --git a/src/assets/images/hotelview/night/night-pool.gif b/src/assets/images/hotelview/night/night-pool.gif new file mode 100644 index 0000000..e5b8493 Binary files /dev/null and b/src/assets/images/hotelview/night/night-pool.gif differ diff --git a/src/assets/images/hotelview/night/night-rooftop.png b/src/assets/images/hotelview/night/night-rooftop.png new file mode 100644 index 0000000..04414d3 Binary files /dev/null and b/src/assets/images/hotelview/night/night-rooftop.png differ diff --git a/src/assets/images/hotelview/night/night-rooftop_pool.gif b/src/assets/images/hotelview/night/night-rooftop_pool.gif new file mode 100644 index 0000000..c24818e Binary files /dev/null and b/src/assets/images/hotelview/night/night-rooftop_pool.gif differ diff --git a/src/assets/images/hotelview/picnic.gif b/src/assets/images/hotelview/picnic.gif index a59d214..b20e179 100644 Binary files a/src/assets/images/hotelview/picnic.gif and b/src/assets/images/hotelview/picnic.gif differ diff --git a/src/components/hotel-view/HotelView.tsx b/src/components/hotel-view/HotelView.tsx index e466442..03e253e 100644 --- a/src/components/hotel-view/HotelView.tsx +++ b/src/components/hotel-view/HotelView.tsx @@ -1,108 +1,163 @@ import { GetConfiguration } from '@nitrots/nitro-renderer'; -import { FC, useRef, useState, useEffect } from 'react'; +import { FC, useEffect, useMemo, useRef, useState } from 'react'; import { GetConfigurationValue } from '../../api'; import { RoomWidgetView } from './RoomWidgetView'; -export const HotelView: FC<{}> = props => { - const backgroundColor = GetConfigurationValue('hotelview')['images']['background.colour']; - console.log('Background color:', backgroundColor); +/** + * Configure via renderer-config.json: "timezone.settings": "Europe/Amsterdam" + */ +function getHourInTimezone(timezone: string): number +{ + if(!timezone) return new Date().getHours(); + + try + { + const parts = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + hour: 'numeric', + hour12: false + }).formatToParts(new Date()); + + const h = parts.find(p => p.type === 'hour'); + + return h ? (parseInt(h.value, 10) % 24) : new Date().getHours(); + } + catch + { + return new Date().getHours(); + } +} + +/** + * Maps an hour to a named time period, matching the old UI's ranges: + * 06-09 → morning + * 10-16 → day + * 17-19 → sunset + * 20-23 → evening + * 00-05 → night + */ +function getTimeOfDay(hour: number): string +{ + if(hour > 5 && hour <= 9) return 'morning'; + if(hour > 9 && hour <= 16) return 'day'; + if(hour > 16 && hour <= 19) return 'sunset'; + if(hour > 19 && hour <= 23) return 'evening'; + + return 'night'; +} + +const SKY_COLORS: Record = { + morning: '#E67451', + sunset: '#C76E00', + evening: '#0a0a1e', + night: '#000000', +}; + +export const HotelView: FC<{}> = props => +{ + const configBgColor = GetConfigurationValue('hotelview')['images']['background.colour']; + + const timezone = GetConfigurationValue('timezone.settings', ''); + + const timeOfDay = useMemo(() => + { + const hour = getHourInTimezone(timezone); + + return getTimeOfDay(hour); + }, [ timezone ]); + + /** + const timeOfDay = 'sunset'; + For debuging the diff views + */ + + const skyColor = SKY_COLORS[timeOfDay] ?? configBgColor ?? '#000'; const containerRef = useRef(null); - const [isDragging, setIsDragging] = useState(false); - const [startX, setStartX] = useState(0); - const [startY, setStartY] = useState(0); - const [scrollLeft, setScrollLeft] = useState(0); - const [scrollTop, setScrollTop] = useState(0); + const [ isDragging, setIsDragging ] = useState(false); + const [ startX, setStartX ] = useState(0); + const [ startY, setStartY ] = useState(0); + const [ scrollLeft, setScrollLeft ] = useState(0); + const [ scrollTop, setScrollTop ] = useState(0); - useEffect(() => { - if (containerRef.current) { - const container = containerRef.current; - const contentWidth = 3000; - const contentHeight = 1185; - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight - 55; + useEffect(() => + { + if(!containerRef.current) return; - const initialScrollLeft = (contentWidth - viewportWidth) / 2; - const initialScrollTop = (contentHeight - viewportHeight) / 2; + const container = containerRef.current; + const contentWidth = 3000; + const contentHeight = 1185; + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight - 55; - container.scrollLeft = Math.max(0, initialScrollLeft); - container.scrollTop = Math.max(0, initialScrollTop); + container.scrollLeft = Math.max(0, (contentWidth - viewportWidth) / 2); + container.scrollTop = Math.max(0, (contentHeight - viewportHeight) / 2); - setScrollLeft(container.scrollLeft); - setScrollTop(container.scrollTop); - } + setScrollLeft(container.scrollLeft); + setScrollTop(container.scrollTop); }, []); - const handleMouseDown = (e: React.MouseEvent) => { - if (e.button !== 0) return; + const handleMouseDown = (e: React.MouseEvent) => + { + if(e.button !== 0) return; + setIsDragging(true); setStartX(e.pageX + scrollLeft); setStartY(e.pageY + scrollTop); - if (containerRef.current) { - containerRef.current.style.cursor = 'grabbing'; - } + + if(containerRef.current) containerRef.current.style.cursor = 'grabbing'; }; - const handleMouseMove = (e: React.MouseEvent) => { - if (!isDragging) return; - e.preventDefault(); - const x = e.pageX; - const y = e.pageY; - const newScrollLeft = startX - x; - const newScrollTop = startY - y; + const handleMouseMove = (e: React.MouseEvent) => + { + if(!isDragging) return; - if (containerRef.current) { + e.preventDefault(); + + const newScrollLeft = startX - e.pageX; + const newScrollTop = startY - e.pageY; + + if(containerRef.current) + { containerRef.current.scrollLeft = newScrollLeft; - containerRef.current.scrollTop = newScrollTop; + containerRef.current.scrollTop = newScrollTop; setScrollLeft(newScrollLeft); setScrollTop(newScrollTop); } }; - const handleMouseUp = () => { + const handleMouseUp = () => + { setIsDragging(false); - if (containerRef.current) { - containerRef.current.style.cursor = 'grab'; - } - }; - - const handleMouseLeave = () => { - setIsDragging(false); - if (containerRef.current) { - containerRef.current.style.cursor = 'grab'; - } + if(containerRef.current) containerRef.current.style.cursor = 'grab'; }; return ( -
-
-
+
); -}; \ No newline at end of file +}; diff --git a/src/css/hotelview/HotelView.css b/src/css/hotelview/HotelView.css index a0b5154..d7aaab4 100644 --- a/src/css/hotelview/HotelView.css +++ b/src/css/hotelview/HotelView.css @@ -2,12 +2,13 @@ position: relative; width: 100%; height: 100%; + background: linear-gradient(to bottom, transparent 1185px, #51841E 1185px); } .hotelview-background { position: relative; margin-left: -400px; - margin-top: 240px; + margin-top: 0; top: 0px; left: 0px; height: 1185px; @@ -24,7 +25,7 @@ height: calc(100% - 55px); background: #000; color: #000; - box-shadow: 0px 55px 0px 0px #83cce8; + box-shadow: 0px 55px 0px 0px #51841E; z-index: 0; /* Prevent stacking context interference */ } @@ -34,7 +35,7 @@ width: 120px; height: 164px; position: absolute; - top: calc(292px + 40px); + top: calc(52px + 40px); left: calc(1062px - 90px); cursor: pointer; @@ -65,7 +66,7 @@ width: 183px; height: 164px; position: absolute; - top: calc(512px + 40px); + top: calc(272px + 40px); left: calc(1324px - 200px); cursor: pointer; @@ -96,7 +97,7 @@ width: 73px; height: 65px; position: absolute; - top: calc(837px + 40px); + top: calc(597px + 40px); left: calc(1264px - 200px); cursor: pointer; @@ -106,7 +107,7 @@ position: absolute; background-image: url(@/assets/images/hotelview/arrow_down.png); background-repeat: no-repeat; - left: 75px; + left: 19px; top: 0px; display: none; animation: MoveUpDown 1s linear infinite; @@ -127,7 +128,7 @@ width: 575px; height: 436px; position: absolute; - top: calc(480px + 248px); + top: calc(240px + 248px); left: calc(889px - 412px); cursor: pointer; @@ -158,7 +159,7 @@ width: 181px; height: 175px; position: absolute; - top: calc(332px + 241px); + top: calc(92px + 241px); left: calc(1690px - 399px); cursor: pointer; z-index: 5; @@ -192,7 +193,7 @@ width: 384px; height: 269px; position: absolute; - top: calc(359px + 240px); + top: calc(119px + 240px); left: calc(1728px - 400px); cursor: pointer; z-index: 6; @@ -248,7 +249,7 @@ width: 433px; height: 361px; position: absolute; - top: calc(477px + 240px); + top: calc(237px + 240px); left: calc(1633px - 400px); cursor: pointer; z-index: 7; @@ -312,4 +313,91 @@ .left { left: 28% !important; } -} \ No newline at end of file +} + +/* ─── Time-of-day overrides ──────────────────────────────────────────────── + The class hotel-{period} is stamped on .nitro-hotel-view by HotelView.tsx. + Controlled by timezone.settings in renderer-config.json. + Periods: morning (06-09) | day (10-16) | sunset (17-19) | evening (20-23) | night (00-05) +─────────────────────────────────────────────────────────────────────────── */ + +/* Morning: orange-tinted building, warm sky */ +.hotel-morning { + box-shadow: 0px 55px 0px 0px #E67451; +} +.hotel-morning .hotelview-background { + background-image: url(@/assets/images/hotelview/hotelview-morning.png); +} + +/* Day: default — no overrides needed, base styles already use day assets */ + +/* Sunset: warm orange sky + amber-earth ground */ +.hotel-sunset { + box-shadow: 0px 55px 0px 0px #D4651A; +} +.hotel-sunset .hotelview { + background: linear-gradient(to bottom, transparent 1185px, #163635 1185px); +} +.hotel-sunset .hotelview-background { + background-image: url(@/assets/images/hotelview/hotelview-sunset.png); +} + +/* Evening: night building, dark teal sky + ground */ +.hotel-evening { + box-shadow: 0px 55px 0px 0px #163635; +} +.hotel-evening .hotelview { + background: linear-gradient(to bottom, transparent 1185px, #163635 1185px); +} +.hotel-evening .hotelview-background { + background-image: url(@/assets/images/hotelview/hotelview-night.png); +} + +/* Night: night building, dark teal sky + ground */ +.hotel-night { + box-shadow: 0px 55px 0px 0px #163635; +} +.hotel-night .hotelview { + background: linear-gradient(to bottom, transparent 1185px, #163635 1185px); +} +.hotel-night .hotelview-background { + background-image: url(@/assets/images/hotelview/hotelview-night.png); +} + +/* ─── Night / Evening room-widget sprite swaps ─────────────────────────── */ + +.hotel-evening .nitro-hotel-view-rooftop, +.hotel-night .nitro-hotel-view-rooftop { + background-image: url(@/assets/images/hotelview/night/night-rooftop.png); +} + +.hotel-evening .nitro-hotel-view-rooftop-pool, +.hotel-night .nitro-hotel-view-rooftop-pool { + background-image: url(@/assets/images/hotelview/night/night-rooftop_pool.gif); +} + +.hotel-evening .nitro-hotel-view-picnic, +.hotel-night .nitro-hotel-view-picnic { + background-image: url(@/assets/images/hotelview/night/night-picnic.gif); +} + +.hotel-evening .nitro-hotel-view-infobus, +.hotel-night .nitro-hotel-view-infobus { + background-image: url(@/assets/images/hotelview/night/night-infobus.gif); +} + +.hotel-evening .nitro-hotel-view-pool, +.hotel-night .nitro-hotel-view-pool { + background-image: url(@/assets/images/hotelview/night/night-pool.gif); +} + +.hotel-evening .nitro-hotel-view-lobby, +.hotel-night .nitro-hotel-view-lobby { + background-image: url(@/assets/images/hotelview/night/night-lobby.png); +} + +.hotel-evening .nitro-hotel-view-peaceful, +.hotel-night .nitro-hotel-view-peaceful { + background-image: url(@/assets/images/hotelview/night/night-park.png); +} +