mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-24 22:12:17 -03:00
Refactor text rendering and adjust viewport defaults
Introduced a drawTextWithBackground helper to centralize and simplify text rendering with backgrounds in CanvasRenderer (JS/TS). Updated all relevant usages to use this helper. Adjusted initial viewport x/y offsets in Canvas and Canvas.ts for improved default positioning.
This commit is contained in:
@@ -50,8 +50,8 @@ export class Canvas {
|
|||||||
this.onHistoryChange = callbacks.onHistoryChange;
|
this.onHistoryChange = callbacks.onHistoryChange;
|
||||||
this.lastMousePosition = { x: 0, y: 0 };
|
this.lastMousePosition = { x: 0, y: 0 };
|
||||||
this.viewport = {
|
this.viewport = {
|
||||||
x: -(this.width / 4),
|
x: -(this.width / 1.5),
|
||||||
y: -(this.height / 4),
|
y: -(this.height / 2),
|
||||||
zoom: 0.8,
|
zoom: 0.8,
|
||||||
};
|
};
|
||||||
const { canvas: offscreenCanvas, ctx: offscreenCtx } = createCanvas(0, 0, '2d', {
|
const { canvas: offscreenCanvas, ctx: offscreenCtx } = createCanvas(0, 0, '2d', {
|
||||||
|
|||||||
@@ -8,6 +8,36 @@ export class CanvasRenderer {
|
|||||||
this.renderInterval = 1000 / 60;
|
this.renderInterval = 1000 / 60;
|
||||||
this.isDirty = false;
|
this.isDirty = false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Helper function to draw text with background at world coordinates
|
||||||
|
* @param ctx Canvas context
|
||||||
|
* @param text Text to display
|
||||||
|
* @param worldX World X coordinate
|
||||||
|
* @param worldY World Y coordinate
|
||||||
|
* @param options Optional styling options
|
||||||
|
*/
|
||||||
|
drawTextWithBackground(ctx, text, worldX, worldY, options = {}) {
|
||||||
|
const { font = "14px sans-serif", textColor = "white", backgroundColor = "rgba(0, 0, 0, 0.7)", padding = 10, lineHeight = 18 } = options;
|
||||||
|
ctx.save();
|
||||||
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
const screenX = (worldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
||||||
|
const screenY = (worldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
||||||
|
ctx.font = font;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
const lines = text.split('\n');
|
||||||
|
const textMetrics = lines.map(line => ctx.measureText(line));
|
||||||
|
const bgWidth = Math.max(...textMetrics.map(m => m.width)) + padding;
|
||||||
|
const bgHeight = lines.length * lineHeight + 4;
|
||||||
|
ctx.fillStyle = backgroundColor;
|
||||||
|
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
||||||
|
ctx.fillStyle = textColor;
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const yPos = screenY - (bgHeight / 2) + (lineHeight / 2) + (index * lineHeight) + 2;
|
||||||
|
ctx.fillText(line, screenX, yPos);
|
||||||
|
});
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
if (this.renderAnimationFrame) {
|
if (this.renderAnimationFrame) {
|
||||||
this.isDirty = true;
|
this.isDirty = true;
|
||||||
@@ -129,21 +159,9 @@ export class CanvasRenderer {
|
|||||||
const text = `${Math.round(rect.width)}x${Math.round(rect.height)}`;
|
const text = `${Math.round(rect.width)}x${Math.round(rect.height)}`;
|
||||||
const textWorldX = rect.x + rect.width / 2;
|
const textWorldX = rect.x + rect.width / 2;
|
||||||
const textWorldY = rect.y + rect.height + (20 / this.canvas.viewport.zoom);
|
const textWorldY = rect.y + rect.height + (20 / this.canvas.viewport.zoom);
|
||||||
ctx.save();
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY, {
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
backgroundColor: "rgba(0, 128, 0, 0.7)"
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
});
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 10;
|
|
||||||
const bgHeight = 22;
|
|
||||||
ctx.fillStyle = "rgba(0, 128, 0, 0.7)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (interaction.mode === 'movingCanvas' && interaction.canvasMoveRect) {
|
if (interaction.mode === 'movingCanvas' && interaction.canvasMoveRect) {
|
||||||
@@ -158,21 +176,9 @@ export class CanvasRenderer {
|
|||||||
const text = `(${Math.round(rect.x)}, ${Math.round(rect.y)})`;
|
const text = `(${Math.round(rect.x)}, ${Math.round(rect.y)})`;
|
||||||
const textWorldX = rect.x + rect.width / 2;
|
const textWorldX = rect.x + rect.width / 2;
|
||||||
const textWorldY = rect.y - (20 / this.canvas.viewport.zoom);
|
const textWorldY = rect.y - (20 / this.canvas.viewport.zoom);
|
||||||
ctx.save();
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY, {
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
backgroundColor: "rgba(0, 100, 170, 0.7)"
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
});
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 10;
|
|
||||||
const bgHeight = 22;
|
|
||||||
ctx.fillStyle = "rgba(0, 100, 170, 0.7)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
renderLayerInfo(ctx) {
|
renderLayerInfo(ctx) {
|
||||||
@@ -214,26 +220,7 @@ export class CanvasRenderer {
|
|||||||
const padding = 20 / this.canvas.viewport.zoom;
|
const padding = 20 / this.canvas.viewport.zoom;
|
||||||
const textWorldX = (minX + maxX) / 2;
|
const textWorldX = (minX + maxX) / 2;
|
||||||
const textWorldY = maxY + padding;
|
const textWorldY = maxY + padding;
|
||||||
ctx.save();
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY);
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const lines = text.split('\n');
|
|
||||||
const textMetrics = lines.map(line => ctx.measureText(line));
|
|
||||||
const textBgWidth = Math.max(...textMetrics.map(m => m.width)) + 10;
|
|
||||||
const lineHeight = 18;
|
|
||||||
const textBgHeight = lines.length * lineHeight + 4;
|
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
|
|
||||||
ctx.fillRect(screenX - textBgWidth / 2, screenY - textBgHeight / 2, textBgWidth, textBgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
lines.forEach((line, index) => {
|
|
||||||
const yPos = screenY - (textBgHeight / 2) + (lineHeight / 2) + (index * lineHeight) + 2;
|
|
||||||
ctx.fillText(line, screenX, yPos);
|
|
||||||
});
|
|
||||||
ctx.restore();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,6 +254,11 @@ export class CanvasRenderer {
|
|||||||
ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.setLineDash([]);
|
ctx.setLineDash([]);
|
||||||
|
// Display dimensions under outputAreaBounds
|
||||||
|
const dimensionsText = `${Math.round(bounds.width)}x${Math.round(bounds.height)}`;
|
||||||
|
const textWorldX = bounds.x + bounds.width / 2;
|
||||||
|
const textWorldY = bounds.y + bounds.height + (20 / this.canvas.viewport.zoom);
|
||||||
|
this.drawTextWithBackground(ctx, dimensionsText, textWorldX, textWorldY);
|
||||||
if (this.canvas.outputAreaShape) {
|
if (this.canvas.outputAreaShape) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
|
||||||
@@ -392,20 +384,11 @@ export class CanvasRenderer {
|
|||||||
// Add text label to show this is the mask drawing area
|
// Add text label to show this is the mask drawing area
|
||||||
const textWorldX = maskBounds.x + maskBounds.width / 2;
|
const textWorldX = maskBounds.x + maskBounds.width / 2;
|
||||||
const textWorldY = maskBounds.y - (10 / this.canvas.viewport.zoom);
|
const textWorldY = maskBounds.y - (10 / this.canvas.viewport.zoom);
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
this.drawTextWithBackground(ctx, "Mask Drawing Area", textWorldX, textWorldY, {
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
font: "12px sans-serif",
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
backgroundColor: "rgba(255, 100, 100, 0.8)",
|
||||||
ctx.font = "12px sans-serif";
|
padding: 8
|
||||||
ctx.textAlign = "center";
|
});
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const text = "Mask Drawing Area";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 8;
|
|
||||||
const bgHeight = 18;
|
|
||||||
ctx.fillStyle = "rgba(255, 100, 100, 0.8)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ export class Canvas {
|
|||||||
this.lastMousePosition = {x: 0, y: 0};
|
this.lastMousePosition = {x: 0, y: 0};
|
||||||
|
|
||||||
this.viewport = {
|
this.viewport = {
|
||||||
x: -(this.width / 4),
|
x: -(this.width / 1.5),
|
||||||
y: -(this.height / 4),
|
y: -(this.height / 2),
|
||||||
zoom: 0.8,
|
zoom: 0.8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,49 @@ export class CanvasRenderer {
|
|||||||
this.isDirty = false;
|
this.isDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to draw text with background at world coordinates
|
||||||
|
* @param ctx Canvas context
|
||||||
|
* @param text Text to display
|
||||||
|
* @param worldX World X coordinate
|
||||||
|
* @param worldY World Y coordinate
|
||||||
|
* @param options Optional styling options
|
||||||
|
*/
|
||||||
|
drawTextWithBackground(ctx: any, text: string, worldX: number, worldY: number, options: any = {}) {
|
||||||
|
const {
|
||||||
|
font = "14px sans-serif",
|
||||||
|
textColor = "white",
|
||||||
|
backgroundColor = "rgba(0, 0, 0, 0.7)",
|
||||||
|
padding = 10,
|
||||||
|
lineHeight = 18
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
const screenX = (worldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
||||||
|
const screenY = (worldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
||||||
|
|
||||||
|
ctx.font = font;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
|
||||||
|
const lines = text.split('\n');
|
||||||
|
const textMetrics = lines.map(line => ctx.measureText(line));
|
||||||
|
const bgWidth = Math.max(...textMetrics.map(m => m.width)) + padding;
|
||||||
|
const bgHeight = lines.length * lineHeight + 4;
|
||||||
|
|
||||||
|
ctx.fillStyle = backgroundColor;
|
||||||
|
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
||||||
|
|
||||||
|
ctx.fillStyle = textColor;
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const yPos = screenY - (bgHeight / 2) + (lineHeight / 2) + (index * lineHeight) + 2;
|
||||||
|
ctx.fillText(line, screenX, yPos);
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.renderAnimationFrame) {
|
if (this.renderAnimationFrame) {
|
||||||
this.isDirty = true;
|
this.isDirty = true;
|
||||||
@@ -156,21 +199,9 @@ export class CanvasRenderer {
|
|||||||
const textWorldX = rect.x + rect.width / 2;
|
const textWorldX = rect.x + rect.width / 2;
|
||||||
const textWorldY = rect.y + rect.height + (20 / this.canvas.viewport.zoom);
|
const textWorldY = rect.y + rect.height + (20 / this.canvas.viewport.zoom);
|
||||||
|
|
||||||
ctx.save();
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY, {
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
backgroundColor: "rgba(0, 128, 0, 0.7)"
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
});
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 10;
|
|
||||||
const bgHeight = 22;
|
|
||||||
ctx.fillStyle = "rgba(0, 128, 0, 0.7)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,21 +219,9 @@ export class CanvasRenderer {
|
|||||||
const textWorldX = rect.x + rect.width / 2;
|
const textWorldX = rect.x + rect.width / 2;
|
||||||
const textWorldY = rect.y - (20 / this.canvas.viewport.zoom);
|
const textWorldY = rect.y - (20 / this.canvas.viewport.zoom);
|
||||||
|
|
||||||
ctx.save();
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY, {
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
backgroundColor: "rgba(0, 100, 170, 0.7)"
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
});
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 10;
|
|
||||||
const bgHeight = 22;
|
|
||||||
ctx.fillStyle = "rgba(0, 100, 170, 0.7)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,31 +266,8 @@ export class CanvasRenderer {
|
|||||||
const padding = 20 / this.canvas.viewport.zoom;
|
const padding = 20 / this.canvas.viewport.zoom;
|
||||||
const textWorldX = (minX + maxX) / 2;
|
const textWorldX = (minX + maxX) / 2;
|
||||||
const textWorldY = maxY + padding;
|
const textWorldY = maxY + padding;
|
||||||
ctx.save();
|
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
this.drawTextWithBackground(ctx, text, textWorldX, textWorldY);
|
||||||
|
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
|
||||||
|
|
||||||
ctx.font = "14px sans-serif";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const lines = text.split('\n');
|
|
||||||
const textMetrics = lines.map(line => ctx.measureText(line));
|
|
||||||
const textBgWidth = Math.max(...textMetrics.map(m => m.width)) + 10;
|
|
||||||
const lineHeight = 18;
|
|
||||||
const textBgHeight = lines.length * lineHeight + 4;
|
|
||||||
|
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
|
|
||||||
ctx.fillRect(screenX - textBgWidth / 2, screenY - textBgHeight / 2, textBgWidth, textBgHeight);
|
|
||||||
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
lines.forEach((line, index) => {
|
|
||||||
const yPos = screenY - (textBgHeight / 2) + (lineHeight / 2) + (index * lineHeight) + 2;
|
|
||||||
ctx.fillText(line, screenX, yPos);
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.restore();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +311,13 @@ export class CanvasRenderer {
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.setLineDash([]);
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
// Display dimensions under outputAreaBounds
|
||||||
|
const dimensionsText = `${Math.round(bounds.width)}x${Math.round(bounds.height)}`;
|
||||||
|
const textWorldX = bounds.x + bounds.width / 2;
|
||||||
|
const textWorldY = bounds.y + bounds.height + (20 / this.canvas.viewport.zoom);
|
||||||
|
|
||||||
|
this.drawTextWithBackground(ctx, dimensionsText, textWorldX, textWorldY);
|
||||||
|
|
||||||
if (this.canvas.outputAreaShape) {
|
if (this.canvas.outputAreaShape) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
|
||||||
@@ -462,22 +465,11 @@ export class CanvasRenderer {
|
|||||||
const textWorldX = maskBounds.x + maskBounds.width / 2;
|
const textWorldX = maskBounds.x + maskBounds.width / 2;
|
||||||
const textWorldY = maskBounds.y - (10 / this.canvas.viewport.zoom);
|
const textWorldY = maskBounds.y - (10 / this.canvas.viewport.zoom);
|
||||||
|
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
this.drawTextWithBackground(ctx, "Mask Drawing Area", textWorldX, textWorldY, {
|
||||||
const screenX = (textWorldX - this.canvas.viewport.x) * this.canvas.viewport.zoom;
|
font: "12px sans-serif",
|
||||||
const screenY = (textWorldY - this.canvas.viewport.y) * this.canvas.viewport.zoom;
|
backgroundColor: "rgba(255, 100, 100, 0.8)",
|
||||||
|
padding: 8
|
||||||
ctx.font = "12px sans-serif";
|
});
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
const text = "Mask Drawing Area";
|
|
||||||
const textMetrics = ctx.measureText(text);
|
|
||||||
const bgWidth = textMetrics.width + 8;
|
|
||||||
const bgHeight = 18;
|
|
||||||
|
|
||||||
ctx.fillStyle = "rgba(255, 100, 100, 0.8)";
|
|
||||||
ctx.fillRect(screenX - bgWidth / 2, screenY - bgHeight / 2, bgWidth, bgHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillText(text, screenX, screenY);
|
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user