mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
Changes config from a function that returns an objectinto a more aptly named BaseObservableMapDefaults class
This commit is contained in:
parent
bed66ada88
commit
73b83fdab8
@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {SubscriptionHandle} from "../BaseObservable";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
@ -24,16 +24,16 @@ import {SortedMapList} from "../list/SortedMapList.js";
|
||||
|
||||
|
||||
export class ApplyMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
private _source: BaseObservableMap<K, V>;
|
||||
private _subscription?: SubscriptionHandle;
|
||||
private _apply?: Apply<K, V>;
|
||||
private _config: BaseObservableMapConfig<K, V>;
|
||||
|
||||
|
||||
constructor(source: BaseObservableMap<K, V>, apply?: Apply<K, V>) {
|
||||
super();
|
||||
this._source = source;
|
||||
this._apply = apply;
|
||||
this._config = config<K, V>();
|
||||
}
|
||||
|
||||
hasApply(): boolean {
|
||||
@ -105,19 +105,19 @@ export class ApplyMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> {
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import {JoinedMap} from "../map/JoinedMap";
|
||||
import {MappedMap} from "../map/MappedMap";
|
||||
import {FilteredMap} from "../map/FilteredMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
import {Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
|
||||
export interface IMapObserver<K, V> {
|
||||
onReset(): void;
|
||||
@ -28,13 +28,6 @@ export interface IMapObserver<K, V> {
|
||||
onRemove(key: K, value: V): void
|
||||
}
|
||||
|
||||
export type BaseObservableMapConfig<K, V> = {
|
||||
join(_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V>;
|
||||
mapValues(_this: BaseObservableMap<K, V>, mapper: any, updater?: Updater<V>): MappedMap<K, V>;
|
||||
sortValues(_this: BaseObservableMap<K, V>, comparator: Comparator<V>): SortedMapList;
|
||||
filterValues(_this: BaseObservableMap<K, V>, filter: Filter<K, V>): FilteredMap<K, V>;
|
||||
}
|
||||
|
||||
export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserver<K, V>> {
|
||||
emitReset(): void {
|
||||
for(let h of this._handlers) {
|
||||
@ -62,10 +55,11 @@ export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserve
|
||||
}
|
||||
|
||||
// The following group of functions have a default implementation
|
||||
// in the neighboring `config.ts`. See the comment in that file for
|
||||
// the explanation for why the default implementation isn't defined
|
||||
// here. See the neighboring `ObservableMap.ts` for an example of how
|
||||
// to easily use the default implementation in a class that extends
|
||||
// in the neighboring `BaseObservableMapDefaults.ts`. See the comment
|
||||
// in that file for the explanation for why the default implementation
|
||||
// isn't defined here. See the neighboring `ObservableMap.ts` for an
|
||||
// example of how to easily add the boilerplate for using the default
|
||||
// implementations of these functions in a class that extends
|
||||
// this one (which is most likely what you want to do).
|
||||
abstract join(...otherMaps: Array<typeof this>): JoinedMap<K, V>;
|
||||
abstract mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V>;
|
||||
|
55
src/observable/map/BaseObservableMapDefaults.ts
Normal file
55
src/observable/map/BaseObservableMapDefaults.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2022 Isaiah Becker-Mayer <ibeckermayer@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
|
||||
|
||||
// This class is used as a default implementation of
|
||||
// the respective abstract functions in BaseObservableMap.
|
||||
// It is kept as its own class in its own file in order to avoid a circular
|
||||
// dependency between the classes that extend BaseObservableMap which are
|
||||
// instantiated here (i.e. `new JoinedMap()`).
|
||||
export class BaseObservableMapDefaults<K, V> {
|
||||
join(_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> {
|
||||
return new JoinedMap([_this].concat(otherMaps));
|
||||
}
|
||||
|
||||
mapValues(_this: BaseObservableMap<K, V>, mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> {
|
||||
return new MappedMap(_this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(_this: BaseObservableMap<K, V>, comparator: Comparator<V>): SortedMapList {
|
||||
return new SortedMapList(_this, comparator);
|
||||
}
|
||||
|
||||
filterValues(_this: BaseObservableMap<K, V>, filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return new FilteredMap(_this, filter);
|
||||
}
|
||||
}
|
||||
|
||||
export type Mapper<V> = (
|
||||
value: V,
|
||||
emitSpontaneousUpdate: any,
|
||||
) => V;
|
||||
|
||||
export type Updater<V> = (params: any, mappedValue?: V, value?: V) => void;
|
||||
|
||||
export type Comparator<V> = (a: V, b: V) => number;
|
||||
|
||||
export type Filter<K, V> = (v: V, k: K) => boolean;
|
@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {SubscriptionHandle} from "../BaseObservable";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
|
||||
export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
private _source: BaseObservableMap<K, V>;
|
||||
private _config: BaseObservableMapConfig<K, V>;
|
||||
private _filter: Filter<K, V>;
|
||||
private _included?: Map<K, boolean>;
|
||||
private _subscription?: SubscriptionHandle;
|
||||
@ -32,7 +32,6 @@ export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
|
||||
super();
|
||||
this._source = source;
|
||||
this._filter = filter;
|
||||
this._config = config<K, V>();
|
||||
}
|
||||
|
||||
setFilter(filter: Filter<K, V>): void {
|
||||
@ -165,19 +164,19 @@ export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V>{
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
@ -23,14 +23,13 @@ import {SubscriptionHandle} from "../BaseObservable";
|
||||
|
||||
|
||||
export class JoinedMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
protected _sources: BaseObservableMap<K, V>[];
|
||||
private _config: BaseObservableMapConfig<K, V>;
|
||||
private _subscriptions?: SourceSubscriptionHandler<K, V>[];
|
||||
|
||||
constructor(sources: BaseObservableMap<K, V>[]) {
|
||||
super();
|
||||
this._sources = sources;
|
||||
this._config = config<K, V>();
|
||||
}
|
||||
|
||||
onAdd(source: BaseObservableMap<K, V>, key: K, value: V): void {
|
||||
@ -136,19 +135,19 @@ export class JoinedMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> {
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
@ -25,17 +25,15 @@ import {ILogItem, LabelOrValues} from "../../logging/types";
|
||||
import {LogLevel} from "../../logging/LogFilter";
|
||||
|
||||
export class LogMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
private _source: BaseObservableMap<K, V>;
|
||||
private _subscription?: SubscriptionHandle;
|
||||
private _log: ILogItem;
|
||||
private _config: BaseObservableMapConfig<K, V>
|
||||
|
||||
|
||||
constructor(source: BaseObservableMap<K, V>, log: ILogItem) {
|
||||
super();
|
||||
this._source = source;
|
||||
this._log = log;
|
||||
this._config = config<K, V>();
|
||||
}
|
||||
|
||||
private log(labelOrValues: LabelOrValues, logLevel?: LogLevel): ILogItem {
|
||||
@ -88,19 +86,19 @@ export class LogMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> {
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
@ -26,12 +26,13 @@ so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate
|
||||
how should the mapped value be notified of an update though? and can it then decide to not propagate the update?
|
||||
*/
|
||||
export class MappedMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
private _source: BaseObservableMap<K, V>;
|
||||
private _mapper: Mapper<V>;
|
||||
private _updater?: Updater<V>;
|
||||
private _mappedValues: Map<K, V>;
|
||||
private _subscription?: SubscriptionHandle;
|
||||
private _config: BaseObservableMapConfig<K, V>
|
||||
|
||||
|
||||
constructor(
|
||||
source: BaseObservableMap<K, V>,
|
||||
@ -43,7 +44,6 @@ export class MappedMap<K, V> extends BaseObservableMap<K, V> {
|
||||
this._mapper = mapper;
|
||||
this._updater = updater;
|
||||
this._mappedValues = new Map<K, V>();
|
||||
this._config = config<K, V>();
|
||||
}
|
||||
|
||||
_emitSpontaneousUpdate(key: K, params: any): void {
|
||||
@ -115,18 +115,18 @@ export class MappedMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V>{
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
}
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {config, Mapper, Updater, Comparator, Filter} from "./config";
|
||||
import {BaseObservableMap} from "./BaseObservableMap";
|
||||
import {BaseObservableMapDefaults, Mapper, Updater, Comparator, Filter} from "./BaseObservableMapDefaults";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
@ -23,12 +23,11 @@ import {SortedMapList} from "../list/SortedMapList.js";
|
||||
|
||||
|
||||
export class ObservableMap<K, V> extends BaseObservableMap<K, V> {
|
||||
private _config: BaseObservableMapConfig<K, V>
|
||||
private _defaults = new BaseObservableMapDefaults<K, V>();
|
||||
private readonly _values: Map<K, V>;
|
||||
|
||||
constructor(initialValues?: (readonly [K, V])[]) {
|
||||
super();
|
||||
this._config = config<K, V>();
|
||||
this._values = new Map(initialValues);
|
||||
}
|
||||
|
||||
@ -101,19 +100,19 @@ export class ObservableMap<K, V> extends BaseObservableMap<K, V> {
|
||||
}
|
||||
|
||||
join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
|
||||
return this._config.join(this, ...otherMaps);
|
||||
return this._defaults.join(this, ...otherMaps);
|
||||
}
|
||||
|
||||
mapValues(mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> {
|
||||
return this._config.mapValues(this, mapper, updater);
|
||||
return this._defaults.mapValues(this, mapper, updater);
|
||||
}
|
||||
|
||||
sortValues(comparator: Comparator<V>): SortedMapList {
|
||||
return this._config.sortValues(this, comparator);
|
||||
return this._defaults.sortValues(this, comparator);
|
||||
}
|
||||
|
||||
filterValues(filter: Filter<K, V>): FilteredMap<K, V> {
|
||||
return this._config.filterValues(this, filter);
|
||||
return this._defaults.filterValues(this, filter);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 Isaiah Becker-Mayer <ibeckermayer@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
|
||||
import {FilteredMap} from "./FilteredMap";
|
||||
import {MappedMap} from "./MappedMap";
|
||||
import {JoinedMap} from "./JoinedMap";
|
||||
import {SortedMapList} from "../list/SortedMapList.js";
|
||||
|
||||
|
||||
// This function is used as a default implementation of
|
||||
// the respective abstract functions in BaseObservableMap.
|
||||
// We implement it this way in order to avoid a circular
|
||||
// dependency between the classes that are instantiated here
|
||||
// (i.e. `new JoinedMap()`) and BaseObservableMap (as they extend it).
|
||||
export function config<K, V>(): BaseObservableMapConfig<K, V> {
|
||||
return {
|
||||
join: (_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> => {
|
||||
return new JoinedMap([_this].concat(otherMaps));
|
||||
},
|
||||
mapValues: (_this: BaseObservableMap<K, V>, mapper: Mapper<V>, updater?: Updater<V>): MappedMap<K, V> => {
|
||||
return new MappedMap(_this, mapper, updater);
|
||||
},
|
||||
sortValues: (_this: BaseObservableMap<K, V>, comparator: Comparator<V>): SortedMapList => {
|
||||
return new SortedMapList(_this, comparator);
|
||||
},
|
||||
filterValues: (_this: BaseObservableMap<K, V>, filter: Filter<K, V>): FilteredMap<K, V> => {
|
||||
return new FilteredMap(_this, filter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export type Mapper<V> = (
|
||||
value: V,
|
||||
emitSpontaneousUpdate: any,
|
||||
) => V;
|
||||
|
||||
export type Updater<V> = (params: any, mappedValue?: V, value?: V) => void;
|
||||
|
||||
export type Comparator<V> = (a: V, b: V) => number;
|
||||
|
||||
export type Filter<K, V> = (v: V, k: K) => boolean;
|
Loading…
Reference in New Issue
Block a user