mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PanelEdit: only update when config changes (#33133)
This commit is contained in:
parent
d807fbc9e9
commit
f93b05f506
@ -167,6 +167,7 @@ export enum NullValueMode {
|
||||
* Describes and API for exposing panel specific data configurations.
|
||||
*/
|
||||
export interface DataConfigSource {
|
||||
configRev?: number;
|
||||
getTransformations: () => DataTransformerConfig[] | undefined;
|
||||
getFieldOverrideOptions: () => ApplyFieldOverrideOptions | undefined;
|
||||
}
|
||||
|
@ -30,7 +30,9 @@ export const OptionsPaneOptions: React.FC<Props> = (props) => {
|
||||
|
||||
const [panelFrameOptions, vizOptions, justOverrides] = useMemo(
|
||||
() => [getPanelFrameCategory(props), getVizualizationOptions(props), getFieldOverrideCategories(props)],
|
||||
[props]
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[panel.configRev]
|
||||
);
|
||||
|
||||
const mainBoxElements: React.ReactNode[] = [];
|
||||
|
@ -45,7 +45,7 @@ export function updateSourcePanel(sourcePanel: PanelModel): ThunkResult<void> {
|
||||
export function discardPanelChanges(): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
const { getPanel } = getStore().panelEditor;
|
||||
getPanel().hasChanged = false;
|
||||
getPanel().configRev = 0;
|
||||
dispatch(setDiscardChanges(true));
|
||||
};
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -69,7 +70,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -188,6 +188,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -201,7 +202,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -290,6 +290,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -303,7 +304,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -414,6 +414,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -427,7 +428,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -546,6 +546,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -559,7 +560,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -648,6 +648,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -661,7 +662,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -754,6 +754,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -767,7 +768,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
|
@ -103,6 +103,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -116,7 +117,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -134,6 +134,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -147,7 +148,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 2,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -165,6 +165,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -178,7 +179,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 3,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -196,6 +196,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -209,7 +210,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 120,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 4,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -252,6 +252,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
panel={
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -265,7 +266,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -344,6 +344,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -357,7 +358,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -375,6 +375,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -388,7 +389,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 2,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -406,6 +406,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -419,7 +420,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 3,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -437,6 +437,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -450,7 +451,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 120,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 4,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -493,6 +493,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
panel={
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -506,7 +507,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 2,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -585,6 +585,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -598,7 +599,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -616,6 +616,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -629,7 +630,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 2,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -647,6 +647,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -660,7 +661,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 3,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -678,6 +678,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -691,7 +692,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 120,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 4,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -734,6 +734,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
panel={
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -747,7 +748,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 3,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -826,6 +826,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"panels": Array [
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -839,7 +840,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 1,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -857,6 +857,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -870,7 +871,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 2,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -888,6 +888,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -901,7 +902,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 20,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 3,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -919,6 +919,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
},
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -932,7 +933,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 120,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 4,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
@ -975,6 +975,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
panel={
|
||||
PanelModel {
|
||||
"cachedPluginOptions": Object {},
|
||||
"configRev": 0,
|
||||
"datasource": null,
|
||||
"events": EventBusSrv {
|
||||
"emitter": EventEmitter {
|
||||
@ -988,7 +989,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
|
||||
"x": 0,
|
||||
"y": 120,
|
||||
},
|
||||
"hasChanged": false,
|
||||
"id": 4,
|
||||
"isEditing": false,
|
||||
"isInView": false,
|
||||
|
@ -59,7 +59,7 @@ const notPersistedProperties: { [str: string]: boolean } = {
|
||||
queryRunner: true,
|
||||
replaceVariables: true,
|
||||
editSourceId: true,
|
||||
hasChanged: true,
|
||||
configRev: true,
|
||||
getDisplayTitle: true,
|
||||
};
|
||||
|
||||
@ -162,7 +162,7 @@ export class PanelModel implements DataConfigSource {
|
||||
isViewing = false;
|
||||
isEditing = false;
|
||||
isInView = false;
|
||||
hasChanged = false;
|
||||
configRev = 0; // increments when configs change
|
||||
|
||||
hasRefreshed?: boolean;
|
||||
events: EventBus;
|
||||
@ -232,16 +232,20 @@ export class PanelModel implements DataConfigSource {
|
||||
return this.fieldConfig;
|
||||
}
|
||||
|
||||
get hasChanged(): boolean {
|
||||
return this.configRev > 0;
|
||||
}
|
||||
|
||||
updateOptions(options: object) {
|
||||
this.options = options;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
this.events.publish(new PanelOptionsChangedEvent());
|
||||
this.render();
|
||||
}
|
||||
|
||||
updateFieldConfig(config: FieldConfigSource) {
|
||||
this.fieldConfig = config;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
this.events.publish(new PanelOptionsChangedEvent());
|
||||
|
||||
this.resendLastResult();
|
||||
@ -398,7 +402,7 @@ export class PanelModel implements DataConfigSource {
|
||||
// switch
|
||||
this.type = pluginId;
|
||||
this.plugin = newPlugin;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
|
||||
// For some reason I need to rebind replace variables here, otherwise the viz repeater does not work
|
||||
this.replaceVariables = this.replaceVariables.bind(this);
|
||||
@ -417,7 +421,7 @@ export class PanelModel implements DataConfigSource {
|
||||
this.interval = options.minInterval;
|
||||
this.maxDataPoints = options.maxDataPoints;
|
||||
this.targets = options.queries;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
|
||||
this.events.publish(new PanelQueriesChangedEvent());
|
||||
}
|
||||
@ -426,13 +430,13 @@ export class PanelModel implements DataConfigSource {
|
||||
query = query || { refId: 'A' };
|
||||
query.refId = getNextRefIdChar(this.targets);
|
||||
this.targets.push(query as DataQuery);
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
}
|
||||
|
||||
changeQuery(query: DataQuery, index: number) {
|
||||
// ensure refId is maintained
|
||||
query.refId = this.targets[index].refId;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
|
||||
// update query in array
|
||||
this.targets = this.targets.map((item, itemIndex) => {
|
||||
@ -503,13 +507,13 @@ export class PanelModel implements DataConfigSource {
|
||||
setTransformations(transformations: DataTransformerConfig[]) {
|
||||
this.transformations = transformations;
|
||||
this.resendLastResult();
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
this.events.publish(new PanelTransformationsChangedEvent());
|
||||
}
|
||||
|
||||
setProperty(key: keyof this, value: any) {
|
||||
this[key] = value;
|
||||
this.hasChanged = true;
|
||||
this.configRev++;
|
||||
|
||||
// Custom handling of repeat dependent options, handled here as PanelEditor can
|
||||
// update one key at a time right now
|
||||
|
@ -45,7 +45,7 @@ export const PanelLibraryOptionsGroup: FC<Props> = ({ panel, searchQuery }) => {
|
||||
libraryPanel: toPanelModelLibraryPanel(changeToPanel),
|
||||
});
|
||||
|
||||
panel.hasChanged = false;
|
||||
panel.configRev = 0;
|
||||
panel.refresh();
|
||||
panel.events.publish(new PanelQueriesChangedEvent());
|
||||
panel.events.publish(new PanelOptionsChangedEvent());
|
||||
|
Loading…
Reference in New Issue
Block a user