@loafmarkets/ui 0.1.426 → 0.1.428
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +33 -43
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -43
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3019,7 +3019,7 @@ PropertyTour.displayName = "PropertyTour";
|
|
|
3019
3019
|
var BREAKING_WINDOW_HOURS = 6;
|
|
3020
3020
|
var BREAKING_MEMORY_CAP = 50;
|
|
3021
3021
|
var BREAKING_MEMORY_STORAGE_KEY = "loaf:dismissed-breaking";
|
|
3022
|
-
var emptyBreakingMemory = () => ({ dismissed: {},
|
|
3022
|
+
var emptyBreakingMemory = () => ({ dismissed: {}, blocked: {} });
|
|
3023
3023
|
var isTimestampMap = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((t) => typeof t === "number" && Number.isFinite(t));
|
|
3024
3024
|
var pruneMap = (map, cutoff) => {
|
|
3025
3025
|
const fresh = Object.entries(map).filter(([, t]) => t >= cutoff);
|
|
@@ -3030,44 +3030,30 @@ var prune = (mem, now) => {
|
|
|
3030
3030
|
const cutoff = now - BREAKING_WINDOW_HOURS * 36e5;
|
|
3031
3031
|
return {
|
|
3032
3032
|
dismissed: pruneMap(mem.dismissed, cutoff),
|
|
3033
|
-
|
|
3034
|
-
// An expired watermark can't block anything (every in-window story is newer).
|
|
3035
|
-
watermark: mem.watermark !== null && mem.watermark >= cutoff ? mem.watermark : null
|
|
3033
|
+
blocked: pruneMap(mem.blocked, cutoff)
|
|
3036
3034
|
};
|
|
3037
3035
|
};
|
|
3038
3036
|
function parseBreakingMemory(raw, now) {
|
|
3039
3037
|
if (!raw) return emptyBreakingMemory();
|
|
3040
3038
|
try {
|
|
3041
3039
|
const v = JSON.parse(raw);
|
|
3042
|
-
if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)
|
|
3040
|
+
if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)) {
|
|
3043
3041
|
return emptyBreakingMemory();
|
|
3044
3042
|
}
|
|
3045
|
-
|
|
3043
|
+
const blocked = isTimestampMap(v.blocked) ? v.blocked : {};
|
|
3044
|
+
return prune({ dismissed: v.dismissed, blocked }, now);
|
|
3046
3045
|
} catch {
|
|
3047
3046
|
return emptyBreakingMemory();
|
|
3048
3047
|
}
|
|
3049
3048
|
}
|
|
3050
3049
|
var serializeBreakingMemory = (mem) => JSON.stringify(mem);
|
|
3051
|
-
function recordBreakingDismissal(mem, key, publishedAtMs, now) {
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
pinned: mem.pinned,
|
|
3056
|
-
watermark: Math.max(mem.watermark ?? -Infinity, publishedAtMs)
|
|
3057
|
-
},
|
|
3058
|
-
now
|
|
3059
|
-
);
|
|
3050
|
+
function recordBreakingDismissal(mem, key, publishedAtMs, now, waiting = []) {
|
|
3051
|
+
const blocked = { ...mem.blocked };
|
|
3052
|
+
for (const w of waiting) blocked[w.key] = w.publishedAtMs;
|
|
3053
|
+
return prune({ dismissed: { ...mem.dismissed, [key]: publishedAtMs }, blocked }, now);
|
|
3060
3054
|
}
|
|
3061
|
-
function
|
|
3062
|
-
|
|
3063
|
-
const pinned = { ...mem.pinned };
|
|
3064
|
-
for (const p of pins) pinned[p.key] = p.publishedAtMs;
|
|
3065
|
-
return prune({ dismissed: mem.dismissed, pinned, watermark: mem.watermark }, now);
|
|
3066
|
-
}
|
|
3067
|
-
function mayPinBreaking(mem, key, publishedAtMs) {
|
|
3068
|
-
if (mem.dismissed[key] !== void 0) return false;
|
|
3069
|
-
if (mem.watermark === null) return true;
|
|
3070
|
-
return publishedAtMs > mem.watermark || mem.pinned[key] !== void 0;
|
|
3055
|
+
function mayPinBreaking(mem, key) {
|
|
3056
|
+
return mem.dismissed[key] === void 0 && mem.blocked[key] === void 0;
|
|
3071
3057
|
}
|
|
3072
3058
|
function loadBreakingMemory(now) {
|
|
3073
3059
|
try {
|
|
@@ -3652,18 +3638,8 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
3652
3638
|
};
|
|
3653
3639
|
const pinnedBreaking = React9.useMemo(() => {
|
|
3654
3640
|
const mem = getBreakingMemory();
|
|
3655
|
-
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it)
|
|
3641
|
+
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
|
|
3656
3642
|
}, [breakingEligible, dismissedVersion]);
|
|
3657
|
-
React9.useEffect(() => {
|
|
3658
|
-
if (pinnedBreaking.length === 0) return;
|
|
3659
|
-
const mem = getBreakingMemory();
|
|
3660
|
-
const next = recordBreakingPins(
|
|
3661
|
-
mem,
|
|
3662
|
-
pinnedBreaking.map((it) => ({ key: breakingKey(it), publishedAtMs: breakingPublishedMs(it) })),
|
|
3663
|
-
Date.now()
|
|
3664
|
-
);
|
|
3665
|
-
if (next !== mem) commitBreakingMemory(next);
|
|
3666
|
-
}, [pinnedBreaking]);
|
|
3667
3643
|
const breakingRowKeys = React9.useMemo(
|
|
3668
3644
|
() => new Set(breakingEligible.map(breakingKey)),
|
|
3669
3645
|
[breakingEligible]
|
|
@@ -3769,8 +3745,10 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
3769
3745
|
const k = breakingKey(item);
|
|
3770
3746
|
const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
|
|
3771
3747
|
const persistDismissal = () => {
|
|
3748
|
+
const holding = new Set(pinnedBreaking);
|
|
3749
|
+
const waiting = breakingEligible.filter((c) => !holding.has(c)).map((c) => ({ key: breakingKey(c), publishedAtMs: breakingPublishedMs(c) }));
|
|
3772
3750
|
commitBreakingMemory(
|
|
3773
|
-
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
|
|
3751
|
+
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now(), waiting)
|
|
3774
3752
|
);
|
|
3775
3753
|
};
|
|
3776
3754
|
const finishDismiss = () => {
|
|
@@ -10797,6 +10775,7 @@ function GalleryMapSection({
|
|
|
10797
10775
|
const [showVideo, setShowVideo] = useState(false);
|
|
10798
10776
|
const [autoPlaying, setAutoPlaying] = useState(autoPlay);
|
|
10799
10777
|
const [showFullMap, setShowFullMap] = useState(false);
|
|
10778
|
+
const [fullMapMountedFor, setFullMapMountedFor] = useState(null);
|
|
10800
10779
|
const [showFullVideo, setShowFullVideo] = useState(false);
|
|
10801
10780
|
const isMobile = useMaxWidth(768);
|
|
10802
10781
|
const resolvedMapUrl = mapUrl ?? (tokenName ? `/map/${tokenName}?embed=true&zoom=14&hideOthers=true` : "about:blank");
|
|
@@ -10914,7 +10893,10 @@ function GalleryMapSection({
|
|
|
10914
10893
|
] }),
|
|
10915
10894
|
/* @__PURE__ */ jsxs(MapHeaderRight, { children: [
|
|
10916
10895
|
/* @__PURE__ */ jsx("span", { children: propertyLocation }),
|
|
10917
|
-
/* @__PURE__ */ jsxs("button", { type: "button", onClick: () =>
|
|
10896
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: () => {
|
|
10897
|
+
setFullMapMountedFor(resolvedMapUrl);
|
|
10898
|
+
setShowFullMap(true);
|
|
10899
|
+
}, children: [
|
|
10918
10900
|
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { width: 14, height: 14 }, children: /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3" }) }),
|
|
10919
10901
|
"Full Screen"
|
|
10920
10902
|
] })
|
|
@@ -10963,7 +10945,7 @@ function GalleryMapSection({
|
|
|
10963
10945
|
] }),
|
|
10964
10946
|
/* @__PURE__ */ jsx(SuburbDesc, { children: "One of Sydney's most prestigious harbourside suburbs, consistently ranked among Australia's highest-value residential markets. Strong capital growth driven by limited supply, heritage conservation overlays, and international buyer demand." })
|
|
10965
10947
|
] }),
|
|
10966
|
-
|
|
10948
|
+
fullMapMountedFor === resolvedMapUrl && !hideMap && /* @__PURE__ */ jsx(FullMapPopup, { hidden: !showFullMap, onClose: () => setShowFullMap(false), mapUrl: resolvedMapUrl }),
|
|
10967
10949
|
showFullVideo && videoUrl && /* @__PURE__ */ jsxs(FullVideoOverlay, { onClick: () => setShowFullVideo(false), children: [
|
|
10968
10950
|
/* @__PURE__ */ jsx(FullVideoClose, { onClick: () => setShowFullVideo(false), "aria-label": "Close video", children: "\u2715" }),
|
|
10969
10951
|
/* @__PURE__ */ jsx(
|
|
@@ -11093,21 +11075,23 @@ function StackedGallery({ images, onPhotoClick }) {
|
|
|
11093
11075
|
}) })
|
|
11094
11076
|
] });
|
|
11095
11077
|
}
|
|
11096
|
-
function FullMapPopup({ onClose, mapUrl }) {
|
|
11078
|
+
function FullMapPopup({ onClose, mapUrl, hidden = false }) {
|
|
11097
11079
|
useEffect(() => {
|
|
11080
|
+
if (hidden) return;
|
|
11098
11081
|
const handleEsc = (e) => {
|
|
11099
11082
|
if (e.key === "Escape") onClose();
|
|
11100
11083
|
};
|
|
11101
11084
|
window.addEventListener("keydown", handleEsc);
|
|
11102
11085
|
return () => window.removeEventListener("keydown", handleEsc);
|
|
11103
|
-
}, [onClose]);
|
|
11086
|
+
}, [onClose, hidden]);
|
|
11104
11087
|
useEffect(() => {
|
|
11088
|
+
if (hidden) return;
|
|
11105
11089
|
const handleMessage = (e) => {
|
|
11106
11090
|
if (e.data === "loaf:close-embed") onClose();
|
|
11107
11091
|
};
|
|
11108
11092
|
window.addEventListener("message", handleMessage);
|
|
11109
11093
|
return () => window.removeEventListener("message", handleMessage);
|
|
11110
|
-
}, [onClose]);
|
|
11094
|
+
}, [onClose, hidden]);
|
|
11111
11095
|
const iframeRef = useCallback((node) => {
|
|
11112
11096
|
if (!node) return;
|
|
11113
11097
|
const handleLoad = () => {
|
|
@@ -11123,7 +11107,7 @@ function FullMapPopup({ onClose, mapUrl }) {
|
|
|
11123
11107
|
};
|
|
11124
11108
|
node.addEventListener("load", handleLoad);
|
|
11125
11109
|
}, [onClose]);
|
|
11126
|
-
return /* @__PURE__ */ jsx(FullMapOverlay, { onClick: onClose, children: /* @__PURE__ */ jsxs(FullMapPanel, { onClick: (e) => e.stopPropagation(), children: [
|
|
11110
|
+
return /* @__PURE__ */ jsx(FullMapOverlay, { onClick: onClose, hidden, children: /* @__PURE__ */ jsxs(FullMapPanel, { onClick: (e) => e.stopPropagation(), children: [
|
|
11127
11111
|
/* @__PURE__ */ jsxs(FullMapHeader, { children: [
|
|
11128
11112
|
/* @__PURE__ */ jsxs(FullMapTitle, { children: [
|
|
11129
11113
|
/* @__PURE__ */ jsx(BiMap, {}),
|
|
@@ -11419,6 +11403,12 @@ var FullMapOverlay = styled10.div`
|
|
|
11419
11403
|
align-items: center;
|
|
11420
11404
|
justify-content: center;
|
|
11421
11405
|
backdrop-filter: blur(4px);
|
|
11406
|
+
|
|
11407
|
+
/* Kept mounted between opens (see fullMapMountedFor); \`display: flex\` above would
|
|
11408
|
+
otherwise beat the UA's [hidden] rule. */
|
|
11409
|
+
&[hidden] {
|
|
11410
|
+
display: none;
|
|
11411
|
+
}
|
|
11422
11412
|
`;
|
|
11423
11413
|
var FullVideoOverlay = styled10.div`
|
|
11424
11414
|
position: fixed;
|