diff --git a/src/main/java/dev/kruhlmann/imgfloat/model/CanvasEvent.java b/src/main/java/dev/kruhlmann/imgfloat/model/CanvasEvent.java new file mode 100644 index 0000000..59a9199 --- /dev/null +++ b/src/main/java/dev/kruhlmann/imgfloat/model/CanvasEvent.java @@ -0,0 +1,32 @@ +package dev.kruhlmann.imgfloat.model; + +public class CanvasEvent { + + public enum Type { + CANVAS, + } + + private Type type; + private String channel; + private CanvasSettingsRequest payload; + + public static CanvasEvent updated(String channel, CanvasSettingsRequest payload) { + CanvasEvent event = new CanvasEvent(); + event.type = Type.CANVAS; + event.channel = channel; + event.payload = payload; + return event; + } + + public Type getType() { + return type; + } + + public String getChannel() { + return channel; + } + + public CanvasSettingsRequest getPayload() { + return payload; + } +} diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java index c6c94f2..01627ba 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java @@ -7,6 +7,7 @@ import dev.kruhlmann.imgfloat.model.Asset; import dev.kruhlmann.imgfloat.model.AssetEvent; import dev.kruhlmann.imgfloat.model.AssetPatch; import dev.kruhlmann.imgfloat.model.AssetView; +import dev.kruhlmann.imgfloat.model.CanvasEvent; import dev.kruhlmann.imgfloat.model.CanvasSettingsRequest; import dev.kruhlmann.imgfloat.model.Channel; import dev.kruhlmann.imgfloat.model.PlaybackRequest; @@ -121,7 +122,9 @@ public class ChannelDirectoryService { channel.setCanvasWidth(req.getWidth()); channel.setCanvasHeight(req.getHeight()); channelRepository.save(channel); - return new CanvasSettingsRequest(channel.getCanvasWidth(), channel.getCanvasHeight()); + CanvasSettingsRequest response = new CanvasSettingsRequest(channel.getCanvasWidth(), channel.getCanvasHeight()); + messagingTemplate.convertAndSend(topicFor(broadcaster), CanvasEvent.updated(broadcaster, response)); + return response; } public Optional createAsset(String broadcaster, MultipartFile file) throws IOException { diff --git a/src/main/resources/static/js/admin.js b/src/main/resources/static/js/admin.js index 27e22e3..5eca481 100644 --- a/src/main/resources/static/js/admin.js +++ b/src/main/resources/static/js/admin.js @@ -2,8 +2,6 @@ const canvas = document.getElementById("admin-canvas"); const ctx = canvas.getContext("2d"); const overlay = document.getElementById("admin-overlay"); let canvasSettings = { width: 1920, height: 1080 }; -canvas.width = canvasSettings.width; -canvas.height = canvasSettings.height; const assets = new Map(); const mediaCache = new Map(); const renderStates = new Map(); @@ -66,6 +64,8 @@ let interactionState = null; let lastSizeInputChanged = null; let stompClient; +applyCanvasSettings(canvasSettings); + audioUnlockEvents.forEach((eventName) => { window.addEventListener(eventName, () => { if (!pendingAudioUnlock.size) return; @@ -467,8 +467,7 @@ function fetchCanvasSettings() { return r.json(); }) .then((settings) => { - canvasSettings = settings; - resizeCanvas(); + applyCanvasSettings(settings); }) .catch(() => { resizeCanvas(); @@ -476,6 +475,16 @@ function fetchCanvasSettings() { }); } +function applyCanvasSettings(settings) { + if (!settings) { + return; + } + const width = Number.isFinite(settings.width) ? settings.width : canvasSettings.width; + const height = Number.isFinite(settings.height) ? settings.height : canvasSettings.height; + canvasSettings = { width, height }; + resizeCanvas(); +} + function resizeCanvas() { if (!overlay) { return; @@ -540,6 +549,10 @@ function updateRenderState(asset) { } function handleEvent(event) { + if (event.type === "CANVAS" && event.payload) { + applyCanvasSettings(event.payload); + return; + } const assetId = event.assetId || event?.patch?.id || event?.payload?.id; if (event.type === "DELETED") { assets.delete(assetId); diff --git a/src/main/resources/static/js/broadcast.js b/src/main/resources/static/js/broadcast.js index 77556b6..283c21c 100644 --- a/src/main/resources/static/js/broadcast.js +++ b/src/main/resources/static/js/broadcast.js @@ -5,8 +5,6 @@ const supportsAnimatedDecode = const canPlayProbe = document.createElement("video"); const ctx = canvas.getContext("2d"); let canvasSettings = { width: 1920, height: 1080 }; -canvas.width = canvasSettings.width; -canvas.height = canvasSettings.height; const assets = new Map(); const mediaCache = new Map(); const renderStates = new Map(); @@ -27,6 +25,8 @@ const pendingRemovals = new Set(); const audioUnlockEvents = ["pointerdown", "keydown", "touchstart"]; let layerOrder = []; +applyCanvasSettings(canvasSettings); + audioUnlockEvents.forEach((eventName) => { window.addEventListener(eventName, () => { if (!pendingAudioUnlock.size) return; @@ -144,8 +144,7 @@ function fetchCanvasSettings() { return r.json(); }) .then((settings) => { - canvasSettings = settings; - resizeCanvas(); + applyCanvasSettings(settings); }) .catch(() => { resizeCanvas(); @@ -153,11 +152,31 @@ function fetchCanvasSettings() { }); } +function applyCanvasSettings(settings) { + if (!settings) { + return; + } + const width = Number.isFinite(settings.width) ? settings.width : canvasSettings.width; + const height = Number.isFinite(settings.height) ? settings.height : canvasSettings.height; + canvasSettings = { width, height }; + resizeCanvas(); +} + function resizeCanvas() { + if (Number.isFinite(canvasSettings.width) && Number.isFinite(canvasSettings.height)) { + canvas.width = canvasSettings.width; + canvas.height = canvasSettings.height; + canvas.style.width = `${canvasSettings.width}px`; + canvas.style.height = `${canvasSettings.height}px`; + } draw(); } function handleEvent(event) { + if (event.type === "CANVAS" && event.payload) { + applyCanvasSettings(event.payload); + return; + } const assetId = event.assetId || event?.patch?.id || event?.payload?.id; if (event.type === "VISIBILITY") { handleVisibilityEvent(event);