Switch to dynamic layout for Dash
Instead of laying out the dash contents "manually" and having the content items explicitly passed their height, just give them a set width.
This commit is contained in:
parent
e9966b4aff
commit
66ea19fbfb
@ -168,17 +168,16 @@ Signals.addSignalMethods(MenuItem.prototype);
|
|||||||
* their name if some search filter is applied.
|
* their name if some search filter is applied.
|
||||||
*
|
*
|
||||||
* width - width available for the display
|
* width - width available for the display
|
||||||
* height - height available for the display
|
|
||||||
*/
|
*/
|
||||||
function AppDisplay(width, height, numberOfColumns, columnGap) {
|
function AppDisplay(width, numberOfColumns, columnGap) {
|
||||||
this._init(width, height, numberOfColumns, columnGap);
|
this._init(width, numberOfColumns, columnGap);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppDisplay.prototype = {
|
AppDisplay.prototype = {
|
||||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||||
|
|
||||||
_init : function(width, height, numberOfColumns, columnGap) {
|
_init : function(width, numberOfColumns, columnGap) {
|
||||||
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
|
GenericDisplay.GenericDisplay.prototype._init.call(this, width, numberOfColumns, columnGap);
|
||||||
|
|
||||||
this._menus = [];
|
this._menus = [];
|
||||||
this._menuDisplays = [];
|
this._menuDisplays = [];
|
||||||
@ -664,6 +663,11 @@ AppWell.prototype = {
|
|||||||
});
|
});
|
||||||
this._favoritesArea.redisplay(favorites);
|
this._favoritesArea.redisplay(favorites);
|
||||||
this._runningArea.redisplay(running);
|
this._runningArea.redisplay(running);
|
||||||
|
// If it's empty, we need to provide a minimum drop target
|
||||||
|
if (running.length == 0)
|
||||||
|
this._runningArea.actor.set_size(this._width, 50);
|
||||||
|
else
|
||||||
|
this._runningArea.actor.set_size(-1, -1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,17 +66,16 @@ DocDisplayItem.prototype = {
|
|||||||
* The documents are sorted by how recently they were last visited.
|
* The documents are sorted by how recently they were last visited.
|
||||||
*
|
*
|
||||||
* width - width available for the display
|
* width - width available for the display
|
||||||
* height - height available for the display
|
|
||||||
*/
|
*/
|
||||||
function DocDisplay(width, height, numberOfColumns, columnGap) {
|
function DocDisplay(width, numberOfColumns, columnGap) {
|
||||||
this._init(width, height, numberOfColumns, columnGap);
|
this._init(width, numberOfColumns, columnGap);
|
||||||
}
|
}
|
||||||
|
|
||||||
DocDisplay.prototype = {
|
DocDisplay.prototype = {
|
||||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||||
|
|
||||||
_init : function(width, height, numberOfColumns, columnGap) {
|
_init : function(width, numberOfColumns, columnGap) {
|
||||||
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
|
GenericDisplay.GenericDisplay.prototype._init.call(this, width, numberOfColumns, columnGap);
|
||||||
let me = this;
|
let me = this;
|
||||||
this._recentManager = Gtk.RecentManager.get_default();
|
this._recentManager = Gtk.RecentManager.get_default();
|
||||||
this._docsStale = true;
|
this._docsStale = true;
|
||||||
|
@ -27,6 +27,8 @@ const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
|
|||||||
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
||||||
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
|
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
|
PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
|
||||||
|
const HOT_PINK_DEBUG = new Clutter.Color();
|
||||||
|
HOT_PINK_DEBUG.from_pixel(0xFF8888FF);
|
||||||
|
|
||||||
const ITEM_DISPLAY_HEIGHT = 50;
|
const ITEM_DISPLAY_HEIGHT = 50;
|
||||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||||
@ -330,18 +332,16 @@ Signals.addSignalMethods(GenericDisplayItem.prototype);
|
|||||||
* that can be filtered with a search string.
|
* that can be filtered with a search string.
|
||||||
*
|
*
|
||||||
* width - width available for the display
|
* width - width available for the display
|
||||||
* height - height available for the display
|
|
||||||
*/
|
*/
|
||||||
function GenericDisplay(width, height, numberOfColumns, columnGap) {
|
function GenericDisplay(width, numberOfColumns, columnGap) {
|
||||||
this._init(width, height, numberOfColumns, columnGap);
|
this._init(width, numberOfColumns, columnGap);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericDisplay.prototype = {
|
GenericDisplay.prototype = {
|
||||||
_init : function(width, height, numberOfColumns, columnGap) {
|
_init : function(width, numberOfColumns, columnGap) {
|
||||||
this._search = '';
|
this._search = '';
|
||||||
this._expanded = false;
|
this._expanded = false;
|
||||||
this._width = null;
|
this._width = null;
|
||||||
this._height = null;
|
|
||||||
this._columnWidth = null;
|
this._columnWidth = null;
|
||||||
|
|
||||||
this._numberOfColumns = numberOfColumns;
|
this._numberOfColumns = numberOfColumns;
|
||||||
@ -350,9 +350,9 @@ GenericDisplay.prototype = {
|
|||||||
this._columnGap = DEFAULT_COLUMN_GAP;
|
this._columnGap = DEFAULT_COLUMN_GAP;
|
||||||
|
|
||||||
this._maxItemsPerPage = null;
|
this._maxItemsPerPage = null;
|
||||||
this._grid = new Tidy.Grid({width: this._width, height: this._height});
|
this._grid = new Tidy.Grid({width: this._width });
|
||||||
|
|
||||||
this._setDimensionsAndMaxItems(width, 0, height);
|
this._setDimensionsAndMaxItems(width, 0);
|
||||||
|
|
||||||
this._grid.column_major = true;
|
this._grid.column_major = true;
|
||||||
this._grid.column_gap = this._columnGap;
|
this._grid.column_gap = this._columnGap;
|
||||||
@ -375,8 +375,7 @@ GenericDisplay.prototype = {
|
|||||||
orientation: Big.BoxOrientation.HORIZONTAL});
|
orientation: Big.BoxOrientation.HORIZONTAL});
|
||||||
|
|
||||||
this._availableWidthForItemDetails = this._columnWidth;
|
this._availableWidthForItemDetails = this._columnWidth;
|
||||||
this._availableHeightForItemDetails = this._height;
|
this.selectedItemDetails = new Big.Box({});
|
||||||
this.selectedItemDetails = new Big.Box({});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Public methods ////
|
//// Public methods ////
|
||||||
@ -463,12 +462,11 @@ GenericDisplay.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Readjusts display layout and the items displayed based on the new dimensions.
|
// Readjusts display layout and the items displayed based on the new dimensions.
|
||||||
setExpanded: function(expanded, baseWidth, expandWidth, height, numberOfColumns) {
|
setExpanded: function(expanded, baseWidth, expandWidth, numberOfColumns) {
|
||||||
this._expanded = expanded;
|
this._expanded = expanded;
|
||||||
this._numberOfColumns = numberOfColumns;
|
this._numberOfColumns = numberOfColumns;
|
||||||
this._setDimensionsAndMaxItems(baseWidth, expandWidth, height);
|
this._setDimensionsAndMaxItems(baseWidth, expandWidth);
|
||||||
this._grid.width = this._width;
|
this._grid.width = this._width;
|
||||||
this._grid.height = this._height;
|
|
||||||
this._pageDisplayed = 0;
|
this._pageDisplayed = 0;
|
||||||
this._displayMatchedItems(true);
|
this._displayMatchedItems(true);
|
||||||
let gridWidth = this._width;
|
let gridWidth = this._width;
|
||||||
@ -678,25 +676,22 @@ GenericDisplay.prototype = {
|
|||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
// Sets this._width, this._height, this._columnWidth, and this._maxItemsPerPage based on the
|
// Sets this._width, this._columnWidth, and this._maxItemsPerPage based on the
|
||||||
// space available for the display, number of columns, and the number of items it can fit.
|
// space available for the display, number of columns, and the number of items it can fit.
|
||||||
_setDimensionsAndMaxItems: function(baseWidth, expandWidth, height) {
|
_setDimensionsAndMaxItems: function(baseWidth, expandWidth) {
|
||||||
this._width = baseWidth + expandWidth;
|
this._width = baseWidth + expandWidth;
|
||||||
let gridWidth;
|
let gridWidth;
|
||||||
let sideArea = this.getSideArea();
|
let sideArea = this.getSideArea();
|
||||||
if (this._expanded && sideArea) {
|
if (this._expanded && sideArea) {
|
||||||
gridWidth = expandWidth;
|
gridWidth = expandWidth;
|
||||||
sideArea.width = baseWidth;
|
sideArea.width = baseWidth;
|
||||||
sideArea.height = this._height;
|
|
||||||
} else {
|
} else {
|
||||||
gridWidth = this._width;
|
gridWidth = this._width;
|
||||||
}
|
}
|
||||||
this._columnWidth = (gridWidth - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns;
|
this._columnWidth = (gridWidth - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns;
|
||||||
let maxItemsInColumn = Math.floor(height / ITEM_DISPLAY_HEIGHT);
|
let maxItemsInColumn = 5; // Math.floor(height / ITEM_DISPLAY_HEIGHT);
|
||||||
this._maxItemsPerPage = maxItemsInColumn * this._numberOfColumns;
|
this._maxItemsPerPage = maxItemsInColumn * this._numberOfColumns;
|
||||||
this._height = maxItemsInColumn * ITEM_DISPLAY_HEIGHT;
|
|
||||||
this._grid.width = gridWidth;
|
this._grid.width = gridWidth;
|
||||||
this._grid.height = this._height;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getSearchMatchedItems: function() {
|
_getSearchMatchedItems: function() {
|
||||||
|
@ -27,6 +27,7 @@ const BACKGROUND_SCALE = 2;
|
|||||||
|
|
||||||
const LABEL_HEIGHT = 16;
|
const LABEL_HEIGHT = 16;
|
||||||
const DASH_MIN_WIDTH = 250;
|
const DASH_MIN_WIDTH = 250;
|
||||||
|
const DASH_OUTER_PADDING = 4;
|
||||||
const DASH_SECTION_PADDING = 6;
|
const DASH_SECTION_PADDING = 6;
|
||||||
const DASH_SECTION_SPACING = 6;
|
const DASH_SECTION_SPACING = 6;
|
||||||
const DASH_COLUMNS = 1;
|
const DASH_COLUMNS = 1;
|
||||||
@ -114,7 +115,7 @@ function SearchEntry(width) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SearchEntry.prototype = {
|
SearchEntry.prototype = {
|
||||||
_init : function(width) {
|
_init : function() {
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
y_align: Big.BoxAlignment.CENTER,
|
||||||
background_color: DASH_SEARCH_BG_COLOR,
|
background_color: DASH_SEARCH_BG_COLOR,
|
||||||
@ -122,7 +123,6 @@ SearchEntry.prototype = {
|
|||||||
spacing: 4,
|
spacing: 4,
|
||||||
padding_left: 4,
|
padding_left: 4,
|
||||||
padding_right: 4,
|
padding_right: 4,
|
||||||
width: width,
|
|
||||||
height: 24
|
height: 24
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ ItemResults.prototype = {
|
|||||||
// LABEL_HEIGHT is the height of this._resultsText and GenericDisplay.LABEL_HEIGHT is the height
|
// LABEL_HEIGHT is the height of this._resultsText and GenericDisplay.LABEL_HEIGHT is the height
|
||||||
// of the display controls.
|
// of the display controls.
|
||||||
this._displayHeight = resultsHeight - LABEL_HEIGHT - GenericDisplay.LABEL_HEIGHT - DASH_SECTION_SPACING * 2;
|
this._displayHeight = resultsHeight - LABEL_HEIGHT - GenericDisplay.LABEL_HEIGHT - DASH_SECTION_SPACING * 2;
|
||||||
this.display = new displayClass(resultsWidth, this._displayHeight, DASH_COLUMNS, DASH_SECTION_SPACING);
|
this.display = new displayClass(resultsWidth, DASH_COLUMNS, DASH_SECTION_SPACING);
|
||||||
|
|
||||||
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ ItemResults.prototype = {
|
|||||||
_setSearchMode: function() {
|
_setSearchMode: function() {
|
||||||
this.actor.height = this._resultsHeight / NUMBER_OF_SECTIONS_IN_SEARCH;
|
this.actor.height = this._resultsHeight / NUMBER_OF_SECTIONS_IN_SEARCH;
|
||||||
let displayHeight = this._displayHeight - this._resultsHeight * (NUMBER_OF_SECTIONS_IN_SEARCH - 1) / NUMBER_OF_SECTIONS_IN_SEARCH;
|
let displayHeight = this._displayHeight - this._resultsHeight * (NUMBER_OF_SECTIONS_IN_SEARCH - 1) / NUMBER_OF_SECTIONS_IN_SEARCH;
|
||||||
this.display.setExpanded(false, this._resultsWidth, 0, displayHeight, DASH_COLUMNS);
|
this.display.setExpanded(false, this._resultsWidth, 0, DASH_COLUMNS);
|
||||||
this.actor.remove_all();
|
this.actor.remove_all();
|
||||||
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
|
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
|
||||||
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
||||||
@ -190,7 +190,7 @@ ItemResults.prototype = {
|
|||||||
|
|
||||||
_unsetSearchMode: function() {
|
_unsetSearchMode: function() {
|
||||||
this.actor.height = this._resultsHeight;
|
this.actor.height = this._resultsHeight;
|
||||||
this.display.setExpanded(false, this._resultsWidth, 0, this._displayHeight, DASH_COLUMNS);
|
this.display.setExpanded(false, this._resultsWidth, 0, DASH_COLUMNS);
|
||||||
this.actor.remove_all();
|
this.actor.remove_all();
|
||||||
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
|
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
|
||||||
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
||||||
@ -225,8 +225,9 @@ Dash.prototype = {
|
|||||||
this.actor = new Clutter.Group({});
|
this.actor = new Clutter.Group({});
|
||||||
this.actor.height = global.screen_height;
|
this.actor.height = global.screen_height;
|
||||||
|
|
||||||
|
|
||||||
// dashPane, as well as results and details panes need to be reactive so that the clicks in unoccupied places on them
|
// dashPane, as well as results and details panes need to be reactive so that the clicks in unoccupied places on them
|
||||||
// are not passed to the transparent background underneath them. This background is used for the workspaces area when
|
// are not passed to the transparent background underneath them. This background is used for the workspaces area when
|
||||||
// the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user
|
// the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user
|
||||||
// can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane.
|
// can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane.
|
||||||
//
|
//
|
||||||
@ -261,12 +262,22 @@ Dash.prototype = {
|
|||||||
dashBackground.append(dashLeft, Big.BoxPackFlags.EXPAND);
|
dashBackground.append(dashLeft, Big.BoxPackFlags.EXPAND);
|
||||||
dashBackground.append(dashRight, Big.BoxPackFlags.EXPAND);
|
dashBackground.append(dashRight, Big.BoxPackFlags.EXPAND);
|
||||||
dashPane.append(dashShadow, Big.BoxPackFlags.NONE);
|
dashPane.append(dashShadow, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this.actor.add_actor(dashPane);
|
this.actor.add_actor(dashPane);
|
||||||
|
|
||||||
this._searchEntry = new SearchEntry(this._width - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2);
|
this.dashOuterContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
this.actor.add_actor(this._searchEntry.actor);
|
x: 0,
|
||||||
this._searchEntry.actor.set_position(DASH_SECTION_PADDING + DASH_BORDER_WIDTH, dashPane.y + DASH_SECTION_PADDING + DASH_BORDER_WIDTH);
|
y: dashPane.y + DASH_BORDER_WIDTH,
|
||||||
|
width: this._width,
|
||||||
|
height: global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight,
|
||||||
|
padding: DASH_OUTER_PADDING
|
||||||
|
});
|
||||||
|
this.actor.add_actor(this.dashOuterContainer);
|
||||||
|
this.dashContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||||
|
this.dashOuterContainer.append(this.dashContainer, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
|
this._searchEntry = new SearchEntry();
|
||||||
|
this.dashContainer.append(this._searchEntry.actor, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._searchQueued = false;
|
this._searchQueued = false;
|
||||||
this._searchEntry.entry.connect('text-changed', function (se, prop) {
|
this._searchEntry.entry.connect('text-changed', function (se, prop) {
|
||||||
@ -335,9 +346,7 @@ Dash.prototype = {
|
|||||||
font_name: "Sans Bold 14px",
|
font_name: "Sans Bold 14px",
|
||||||
text: "Applications",
|
text: "Applications",
|
||||||
height: LABEL_HEIGHT});
|
height: LABEL_HEIGHT});
|
||||||
this._appsSection = new Big.Box({ x: DASH_SECTION_PADDING,
|
this._appsSection = new Big.Box({ padding_top: DASH_SECTION_PADDING,
|
||||||
y: this._searchEntry.actor.y + this._searchEntry.actor.height + DASH_SECTION_PADDING,
|
|
||||||
padding_top: DASH_SECTION_PADDING,
|
|
||||||
spacing: DASH_SECTION_SPACING});
|
spacing: DASH_SECTION_SPACING});
|
||||||
this._appsSection.append(this._appsText, Big.BoxPackFlags.EXPAND);
|
this._appsSection.append(this._appsText, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
@ -357,22 +366,20 @@ Dash.prototype = {
|
|||||||
moreAppsBox.append(this._moreAppsLink.actor, Big.BoxPackFlags.EXPAND);
|
moreAppsBox.append(this._moreAppsLink.actor, Big.BoxPackFlags.EXPAND);
|
||||||
this._appsSection.append(moreAppsBox, Big.BoxPackFlags.EXPAND);
|
this._appsSection.append(moreAppsBox, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
this.actor.add_actor(this._appsSection);
|
this.dashContainer.append(this._appsSection, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._appsSectionDefaultHeight = this._appsSection.height;
|
this._appsSectionDefaultHeight = this._appsSection.height;
|
||||||
|
|
||||||
this._docsSection = new Big.Box({ x: DASH_SECTION_PADDING,
|
this._docsSection = new Big.Box({ padding_top: DASH_SECTION_PADDING,
|
||||||
y: this._appsSection.y + this._appsSection.height,
|
|
||||||
padding_top: DASH_SECTION_PADDING,
|
|
||||||
spacing: DASH_SECTION_SPACING});
|
spacing: DASH_SECTION_SPACING});
|
||||||
|
|
||||||
this._docsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
|
this._docsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
|
||||||
font_name: "Sans Bold 14px",
|
font_name: "Sans Bold 14px",
|
||||||
text: "Recent Documents",
|
text: "Recent Documents",
|
||||||
height: LABEL_HEIGHT});
|
height: LABEL_HEIGHT});
|
||||||
this._docsSection.append(this._docsText, Big.BoxPackFlags.EXPAND);
|
this._docsSection.append(this._docsText, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._docDisplay = new DocDisplay.DocDisplay(this._displayWidth, this._itemDisplayHeight - this._appsContent.height, DASH_COLUMNS, DASH_SECTION_PADDING);
|
this._docDisplay = new DocDisplay.DocDisplay(this._displayWidth, DASH_COLUMNS, DASH_SECTION_PADDING);
|
||||||
this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
|
this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END});
|
let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END});
|
||||||
@ -383,7 +390,7 @@ Dash.prototype = {
|
|||||||
moreDocsBox.append(this._moreDocsLink.actor, Big.BoxPackFlags.EXPAND);
|
moreDocsBox.append(this._moreDocsLink.actor, Big.BoxPackFlags.EXPAND);
|
||||||
this._docsSection.append(moreDocsBox, Big.BoxPackFlags.EXPAND);
|
this._docsSection.append(moreDocsBox, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
this.actor.add_actor(this._docsSection);
|
this.dashContainer.append(this._docsSection, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
this._docsSectionDefaultHeight = this._docsSection.height;
|
this._docsSectionDefaultHeight = this._docsSection.height;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user