fix ref geometry ref rerollable
This commit is contained in:
@@ -808,7 +808,8 @@ function EditorPageContent() {
|
|||||||
}, [
|
}, [
|
||||||
activeTimelineFilterEnabled,
|
activeTimelineFilterEnabled,
|
||||||
activeTimelineYear,
|
activeTimelineYear,
|
||||||
editor,
|
editor.mainDraft,
|
||||||
|
editor.replayDraft,
|
||||||
isReplayEditMode,
|
isReplayEditMode,
|
||||||
isReplayPreviewMode,
|
isReplayPreviewMode,
|
||||||
isViewerPreviewMode,
|
isViewerPreviewMode,
|
||||||
@@ -1193,7 +1194,6 @@ function EditorPageContent() {
|
|||||||
internalSetMode,
|
internalSetMode,
|
||||||
mode,
|
mode,
|
||||||
resetReplayPreview,
|
resetReplayPreview,
|
||||||
restoreEditorOriginalMapState,
|
|
||||||
selectedFeatureIds,
|
selectedFeatureIds,
|
||||||
setHideOutside,
|
setHideOutside,
|
||||||
setReplayFeatureId,
|
setReplayFeatureId,
|
||||||
@@ -1540,12 +1540,23 @@ function EditorPageContent() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Visibility cuối cùng theo type/layer, có override riêng cho replay edit/preview.
|
// Visibility cuối cùng theo type/layer, có override riêng cho replay edit/preview.
|
||||||
|
const replayMarkerGeometryId = useMemo(() => {
|
||||||
|
if (isReplayPreviewMode) {
|
||||||
|
const id = String(previewSession?.replay?.geometry_id || replayFeatureId || "").trim();
|
||||||
|
return id.length ? id : null;
|
||||||
|
}
|
||||||
|
if (isReplayEditMode && replayFeatureId) {
|
||||||
|
return String(replayFeatureId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [isReplayEditMode, isReplayPreviewMode, previewSession?.replay?.geometry_id, replayFeatureId]);
|
||||||
|
|
||||||
const effectiveGeometryVisibility = useMemo(() => {
|
const effectiveGeometryVisibility = useMemo(() => {
|
||||||
const visibility: Record<string, boolean> = { ...geometryVisibility };
|
const visibility: Record<string, boolean> = { ...geometryVisibility };
|
||||||
|
|
||||||
if ((isReplayEditMode || isReplayPreviewMode) && replayFeatureId) {
|
if ((isReplayEditMode || isReplayPreviewMode) && replayMarkerGeometryId) {
|
||||||
// Ẩn chính geo được chọn làm replay (marker kịch bản)
|
// Ẩn chính geo được chọn làm replay (marker kịch bản)
|
||||||
visibility[String(replayFeatureId)] = false;
|
visibility[replayMarkerGeometryId] = false;
|
||||||
|
|
||||||
if (isReplayEditMode && hideOutside) {
|
if (isReplayEditMode && hideOutside) {
|
||||||
// Trong mode replay, ta chỉ hiển thị những gì có trong draft của replay đó
|
// Trong mode replay, ta chỉ hiển thị những gì có trong draft của replay đó
|
||||||
@@ -1553,7 +1564,7 @@ function EditorPageContent() {
|
|||||||
|
|
||||||
// Ẩn tất cả các geo KHÔNG nằm trong draft replay hiện tại
|
// Ẩn tất cả các geo KHÔNG nằm trong draft replay hiện tại
|
||||||
Object.keys(visibility).forEach(fid => {
|
Object.keys(visibility).forEach(fid => {
|
||||||
if (fid === String(replayFeatureId)) {
|
if (fid === replayMarkerGeometryId) {
|
||||||
visibility[fid] = false;
|
visibility[fid] = false;
|
||||||
} else {
|
} else {
|
||||||
visibility[fid] = currentReplayFeatureIds.has(fid);
|
visibility[fid] = currentReplayFeatureIds.has(fid);
|
||||||
@@ -1569,7 +1580,7 @@ function EditorPageContent() {
|
|||||||
hideOutside,
|
hideOutside,
|
||||||
isReplayEditMode,
|
isReplayEditMode,
|
||||||
isReplayPreviewMode,
|
isReplayPreviewMode,
|
||||||
replayFeatureId,
|
replayMarkerGeometryId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Load project editor payload, xử lý auth và pending-submission lock.
|
// Load project editor payload, xử lý auth và pending-submission lock.
|
||||||
@@ -2422,10 +2433,16 @@ function EditorPageContent() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleRerollGeometryId = useCallback((oldId: string | number) => {
|
const handleRerollGeometryId = useCallback((oldId: string | number) => {
|
||||||
|
const feature = editor.draft.features.find((item) => String(item.properties.id) === String(oldId));
|
||||||
|
if (!feature || feature.properties.source === "ref") {
|
||||||
|
flashEntityFormStatus("Không thể đổi ID geometry ref vì đây là identity từ backend.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nextId = newId();
|
const nextId = newId();
|
||||||
editor.changeFeatureId(oldId, nextId);
|
editor.changeFeatureId(oldId, nextId);
|
||||||
setSelectedFeatureIds((prev) => prev.map((id) => String(id) === String(oldId) ? nextId : id));
|
setSelectedFeatureIds((prev) => prev.map((id) => String(id) === String(oldId) ? nextId : id));
|
||||||
}, [editor, setSelectedFeatureIds]);
|
}, [editor, flashEntityFormStatus, setSelectedFeatureIds]);
|
||||||
|
|
||||||
const handleRerollEntityId = useCallback((oldId: string, nextId: string) => {
|
const handleRerollEntityId = useCallback((oldId: string, nextId: string) => {
|
||||||
const activeEntity = entities.find(e => e.id === oldId);
|
const activeEntity = entities.find(e => e.id === oldId);
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ export default function SelectedGeometryPanel({
|
|||||||
|
|
||||||
if (!selectedFeatures || selectedFeatures.length === 0) return null;
|
if (!selectedFeatures || selectedFeatures.length === 0) return null;
|
||||||
const representativeFeature = selectedFeatures[0];
|
const representativeFeature = selectedFeatures[0];
|
||||||
|
const canRerollGeometryId =
|
||||||
|
!isBulkMode &&
|
||||||
|
representativeFeature.properties.source !== "ref" &&
|
||||||
|
Boolean(onRerollGeometryId);
|
||||||
|
|
||||||
const groupedGeometryTypeOptions = groupGeometryTypeOptions(GEOMETRY_TYPE_OPTIONS);
|
const groupedGeometryTypeOptions = groupGeometryTypeOptions(GEOMETRY_TYPE_OPTIONS);
|
||||||
const featureGeometryPreset = resolveFeatureGeometryPreset(representativeFeature);
|
const featureGeometryPreset = resolveFeatureGeometryPreset(representativeFeature);
|
||||||
@@ -225,7 +229,7 @@ export default function SelectedGeometryPanel({
|
|||||||
<div style={{ color: "#94a3b8", fontSize: "11px", overflowWrap: "anywhere", minWidth: 0, flex: 1 }}>
|
<div style={{ color: "#94a3b8", fontSize: "11px", overflowWrap: "anywhere", minWidth: 0, flex: 1 }}>
|
||||||
{isBulkMode ? `Đang chọn ${selectedFeatures.length} geometries` : `ID: ${representativeFeature.properties.id}`}
|
{isBulkMode ? `Đang chọn ${selectedFeatures.length} geometries` : `ID: ${representativeFeature.properties.id}`}
|
||||||
</div>
|
</div>
|
||||||
{!isBulkMode && onRerollGeometryId && (
|
{canRerollGeometryId && onRerollGeometryId && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onRerollGeometryId(representativeFeature.properties.id)}
|
onClick={() => onRerollGeometryId(representativeFeature.properties.id)}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type FeatureLabelInfo = {
|
|||||||
label: string;
|
label: string;
|
||||||
timeEnd: number | null;
|
timeEnd: number | null;
|
||||||
};
|
};
|
||||||
|
const rasterBaseVisibilityGenerationByMap = new WeakMap<maplibregl.Map, number>();
|
||||||
|
|
||||||
export function applyBackgroundLayerVisibility(
|
export function applyBackgroundLayerVisibility(
|
||||||
map: maplibregl.Map,
|
map: maplibregl.Map,
|
||||||
@@ -47,19 +48,34 @@ export function applyBackgroundLayerVisibility(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function syncRasterBaseVisibility(map: maplibregl.Map, shouldShow: boolean) {
|
export function syncRasterBaseVisibility(map: maplibregl.Map, shouldShow: boolean) {
|
||||||
|
const generation = nextRasterBaseVisibilityGeneration(map);
|
||||||
|
const isCurrentRequest = () => rasterBaseVisibilityGenerationByMap.get(map) === generation;
|
||||||
|
|
||||||
if (shouldShow) {
|
if (shouldShow) {
|
||||||
void ensureRasterBaseLayer(map).catch((error) => {
|
void ensureRasterBaseLayer(map, isCurrentRequest).catch((error) => {
|
||||||
console.error("Failed to load proxied raster background.", error);
|
console.error("Failed to load proxied raster background.", error);
|
||||||
removeRasterBaseLayer(map);
|
if (isCurrentRequest()) {
|
||||||
|
removeRasterBaseLayer(map);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
removeRasterBaseLayer(map);
|
removeRasterBaseLayer(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureRasterBaseLayer(map: maplibregl.Map) {
|
function nextRasterBaseVisibilityGeneration(map: maplibregl.Map) {
|
||||||
|
const next = (rasterBaseVisibilityGenerationByMap.get(map) || 0) + 1;
|
||||||
|
rasterBaseVisibilityGenerationByMap.set(map, next);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureRasterBaseLayer(
|
||||||
|
map: maplibregl.Map,
|
||||||
|
isCurrentRequest: () => boolean = () => true
|
||||||
|
) {
|
||||||
if (!map.getSource(RASTER_BASE_SOURCE_ID)) {
|
if (!map.getSource(RASTER_BASE_SOURCE_ID)) {
|
||||||
const source = await createRasterBaseSource();
|
const source = await createRasterBaseSource();
|
||||||
|
if (!isCurrentRequest()) return;
|
||||||
if (map.getSource(RASTER_BASE_SOURCE_ID)) {
|
if (map.getSource(RASTER_BASE_SOURCE_ID)) {
|
||||||
// Another caller already added the source while we were waiting.
|
// Another caller already added the source while we were waiting.
|
||||||
} else {
|
} else {
|
||||||
@@ -67,6 +83,8 @@ export async function ensureRasterBaseLayer(map: maplibregl.Map) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isCurrentRequest()) return;
|
||||||
|
|
||||||
const beforeId = getRasterBaseInsertBeforeLayerId(map);
|
const beforeId = getRasterBaseInsertBeforeLayerId(map);
|
||||||
if (!map.getLayer(RASTER_BASE_LAYER_ID)) {
|
if (!map.getLayer(RASTER_BASE_LAYER_ID)) {
|
||||||
map.addLayer(createRasterBaseLayer(), beforeId);
|
map.addLayer(createRasterBaseLayer(), beforeId);
|
||||||
@@ -74,6 +92,7 @@ export async function ensureRasterBaseLayer(map: maplibregl.Map) {
|
|||||||
map.moveLayer(RASTER_BASE_LAYER_ID, beforeId);
|
map.moveLayer(RASTER_BASE_LAYER_ID, beforeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isCurrentRequest()) return;
|
||||||
map.setLayoutProperty(RASTER_BASE_LAYER_ID, "visibility", "visible");
|
map.setLayoutProperty(RASTER_BASE_LAYER_ID, "visibility", "visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,18 +34,18 @@ export default function TimelineBar({
|
|||||||
const effectiveDisabled = disabled;
|
const effectiveDisabled = disabled;
|
||||||
const safeYear = clampYearValue(year, lower, upper);
|
const safeYear = clampYearValue(year, lower, upper);
|
||||||
|
|
||||||
const [localYear, setLocalYear] = useState(safeYear);
|
const [localYear, setLocalYear] = useState<number | null>(null);
|
||||||
|
const displayYear = localYear ?? safeYear;
|
||||||
// Đồng bộ prop year với localYear khi prop year thay đổi từ bên ngoài
|
const localYearRef = useRef(displayYear);
|
||||||
useEffect(() => {
|
|
||||||
setLocalYear(safeYear);
|
|
||||||
}, [safeYear]);
|
|
||||||
|
|
||||||
const localYearRef = useRef(localYear);
|
|
||||||
localYearRef.current = localYear;
|
|
||||||
|
|
||||||
const onYearChangeRef = useRef(onYearChange);
|
const onYearChangeRef = useRef(onYearChange);
|
||||||
onYearChangeRef.current = onYearChange;
|
|
||||||
|
useEffect(() => {
|
||||||
|
localYearRef.current = displayYear;
|
||||||
|
}, [displayYear]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onYearChangeRef.current = onYearChange;
|
||||||
|
}, [onYearChange]);
|
||||||
|
|
||||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@@ -66,6 +66,7 @@ export default function TimelineBar({
|
|||||||
|
|
||||||
const handleLocalYearChange = useCallback((nextVal: number) => {
|
const handleLocalYearChange = useCallback((nextVal: number) => {
|
||||||
const clamped = clampYearValue(Math.trunc(nextVal), lower, upper);
|
const clamped = clampYearValue(Math.trunc(nextVal), lower, upper);
|
||||||
|
localYearRef.current = clamped;
|
||||||
setLocalYear(clamped);
|
setLocalYear(clamped);
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -81,6 +82,11 @@ export default function TimelineBar({
|
|||||||
}
|
}
|
||||||
}, [lower, upper, commitYearChange]);
|
}, [lower, upper, commitYearChange]);
|
||||||
|
|
||||||
|
const finishLocalYearChange = useCallback(() => {
|
||||||
|
commitYearChange(localYearRef.current);
|
||||||
|
setLocalYear(null);
|
||||||
|
}, [commitYearChange]);
|
||||||
|
|
||||||
const startChangingYear = (direction: number) => {
|
const startChangingYear = (direction: number) => {
|
||||||
if (effectiveDisabled) return;
|
if (effectiveDisabled) return;
|
||||||
const nextVal = localYearRef.current + direction;
|
const nextVal = localYearRef.current + direction;
|
||||||
@@ -108,7 +114,7 @@ export default function TimelineBar({
|
|||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
intervalRef.current = null;
|
intervalRef.current = null;
|
||||||
}
|
}
|
||||||
commitYearChange(localYearRef.current);
|
finishLocalYearChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -163,10 +169,10 @@ export default function TimelineBar({
|
|||||||
min={lower}
|
min={lower}
|
||||||
max={upper}
|
max={upper}
|
||||||
step={1}
|
step={1}
|
||||||
value={localYear}
|
value={displayYear}
|
||||||
onChange={(event) => handleLocalYearChange(Number(event.target.value))}
|
onChange={(event) => handleLocalYearChange(Number(event.target.value))}
|
||||||
onMouseUp={() => commitYearChange(localYearRef.current)}
|
onMouseUp={finishLocalYearChange}
|
||||||
onTouchEnd={() => commitYearChange(localYearRef.current)}
|
onTouchEnd={finishLocalYearChange}
|
||||||
disabled={effectiveDisabled}
|
disabled={effectiveDisabled}
|
||||||
className={styles.slider}
|
className={styles.slider}
|
||||||
aria-label="Timeline year"
|
aria-label="Timeline year"
|
||||||
@@ -180,12 +186,12 @@ export default function TimelineBar({
|
|||||||
min={lower}
|
min={lower}
|
||||||
max={upper}
|
max={upper}
|
||||||
step={1}
|
step={1}
|
||||||
value={localYear}
|
value={displayYear}
|
||||||
onChange={(event) => handleLocalYearChange(Number(event.target.value))}
|
onChange={(event) => handleLocalYearChange(Number(event.target.value))}
|
||||||
onBlur={() => commitYearChange(localYearRef.current)}
|
onBlur={finishLocalYearChange}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
commitYearChange(localYearRef.current);
|
finishLocalYearChange();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={effectiveDisabled}
|
disabled={effectiveDisabled}
|
||||||
|
|||||||
Reference in New Issue
Block a user