mirror of
https://github.com/Stone-Red-Code/website.git
synced 2026-09-04 09:15:58 +02:00
style: autoformat/lint the codebase
This commit is contained in:
@@ -10,7 +10,7 @@ title: Generics in Java
|
||||
Generics is a concept in programming which allows passing a type argument to a class or a method.
|
||||
|
||||
Java's standard library makes heavy use of generics to reduce repetition and to provide flexibility.
|
||||
|
||||
|
||||
Here's an example where Java uses generics.
|
||||
|
||||
```java
|
||||
@@ -25,7 +25,7 @@ In this example we are passing the String type to the List class. We can now say
|
||||
|
||||
## Why should we use generics?
|
||||
|
||||
By using generics we can provide type safety while having reusable code.
|
||||
By using generics we can provide type safety while having reusable code.
|
||||
|
||||
Lets say we want to create coffee capsule, We want one holder for Espresso capsules and one for Cappuccino capsules. This is one way we can implement this coffee capsule holder.
|
||||
|
||||
@@ -42,7 +42,7 @@ import java.util.ArrayList;
|
||||
// An espresso capsule holder
|
||||
class EspressoHolder {
|
||||
// This is a generic. ArrayList of EspressoCapsules
|
||||
private ArrayList<EspressoCapsule> capsules = new ArrayList<EspressoCapsule>();
|
||||
private ArrayList<EspressoCapsule> capsules = new ArrayList<EspressoCapsule>();
|
||||
|
||||
public EspressoHolder(EspressoCapsule capsule) {
|
||||
this.capsules.add(capsule);
|
||||
@@ -77,7 +77,7 @@ Next up we'll take a look at an implementation which uses generics to avoid this
|
||||
|
||||
## Optimizing our Coffee brewer with Generics
|
||||
|
||||
What if we could have a single Holder class for both types of capsules? Let's implement that by using generics.
|
||||
What if we could have a single Holder class for both types of capsules? Let's implement that by using generics.
|
||||
|
||||
Let's start off by removing both the `CappuccinoHolder` and the `EspressoHolder` classes. We're now left with this:
|
||||
|
||||
@@ -141,4 +141,4 @@ class Main {
|
||||
}
|
||||
```
|
||||
|
||||
We just reduces the amount code required to hold our two capsule types in by 50%. The cool thing is that we're now able to create as many capsule types as we want while being able to stick to our Holder implementation. We could have 20 different capsule types, our `Holder` class would be able to take care of all of them.
|
||||
We just reduces the amount code required to hold our two capsule types in by 50%. The cool thing is that we're now able to create as many capsule types as we want while being able to stick to our Holder implementation. We could have 20 different capsule types, our `Holder` class would be able to take care of all of them.
|
||||
|
||||
@@ -32,20 +32,20 @@ const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const foundUser = users.find(user => user.name === "John")
|
||||
const foundUser = users.find(user => user.name === "John");
|
||||
|
||||
// iterative way
|
||||
let foundUser = null
|
||||
let foundUser = null;
|
||||
users.forEach(user => {
|
||||
if (user.name === "John") {
|
||||
foundUser = user
|
||||
foundUser = user;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(foundUser) // outputs { name: "John", age: 17 }
|
||||
console.log(foundUser); // outputs { name: "John", age: 17 }
|
||||
```
|
||||
|
||||
## Keep specific items using `filter()`
|
||||
@@ -68,20 +68,20 @@ const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const youngerUsers = users.filter(user => user.age < 18)
|
||||
const youngerUsers = users.filter(user => user.age < 18);
|
||||
|
||||
// iterative way
|
||||
const youngerUsers = []
|
||||
const youngerUsers = [];
|
||||
users.forEach(user => {
|
||||
if (user.age < 18) {
|
||||
youngerUsers.push(user)
|
||||
youngerUsers.push(user);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(youngerUsers) // outputs [ { name: "John", age: 17 }, { name: "Jane", age: 16 } ]
|
||||
console.log(youngerUsers); // outputs [ { name: "John", age: 17 }, { name: "Jane", age: 16 } ]
|
||||
```
|
||||
|
||||
## Modifying all elements of an array using `.map()`
|
||||
@@ -102,18 +102,18 @@ const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const userNames = users.map(user => user.name)
|
||||
const userNames = users.map(user => user.name);
|
||||
|
||||
// iterative way
|
||||
const userNames = []
|
||||
const userNames = [];
|
||||
users.forEach(user => {
|
||||
userNames.push(user.name)
|
||||
})
|
||||
userNames.push(user.name);
|
||||
});
|
||||
|
||||
console.log(userNames) // outputs [ "Joe", "John", "Jane" ]
|
||||
console.log(userNames); // outputs [ "Joe", "John", "Jane" ]
|
||||
```
|
||||
|
||||
## Running custom logic using an array using `.reduce()`
|
||||
@@ -133,18 +133,18 @@ const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const totalAge = users.reduce((acc, user) => acc + user.age, 0)
|
||||
const totalAge = users.reduce((acc, user) => acc + user.age, 0);
|
||||
|
||||
// iterative way
|
||||
let totalAge = 0
|
||||
let totalAge = 0;
|
||||
users.forEach(user => {
|
||||
totalAge += user.age
|
||||
})
|
||||
totalAge += user.age;
|
||||
});
|
||||
|
||||
console.log(totalAge) // outputs 58
|
||||
console.log(totalAge); // outputs 58
|
||||
```
|
||||
|
||||
## Checking if any element matches a condition with `some()`
|
||||
@@ -163,47 +163,47 @@ const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const hasYoungUsers = users.some(user => user.age < 18)
|
||||
const hasYoungUsers = users.some(user => user.age < 18);
|
||||
|
||||
// iterative way
|
||||
let hasYoungUsers = false
|
||||
let hasYoungUsers = false;
|
||||
for (let i = 0; users.length > i; i++) {
|
||||
if (user > 18) {
|
||||
hasYoungUsers = true
|
||||
break
|
||||
hasYoungUsers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(hasYoungUsers) // outputs true
|
||||
console.log(hasYoungUsers); // outputs true
|
||||
```
|
||||
|
||||
## every
|
||||
|
||||
`every()` is similar to `some()`. The difference is that `some()` test if one or more of the items match. `every()` tests if every item in the array matches.
|
||||
|
||||
|
||||
```js
|
||||
// given our data
|
||||
const users = [
|
||||
{ name: "Joe", age: 25 },
|
||||
{ name: "John", age: 17 },
|
||||
{ name: "Jane", age: 16 },
|
||||
]
|
||||
];
|
||||
|
||||
// functional way
|
||||
const allUsersAreOldEnough = users.every(user => user.age < 18)
|
||||
const allUsersAreOldEnough = users.every(user => user.age < 18);
|
||||
|
||||
// iterative way
|
||||
let allUsersAreOldEnough = true
|
||||
let allUsersAreOldEnough = true;
|
||||
for (let i = 0; users.length > i; i++) {
|
||||
if (user.age > 18) {
|
||||
allUsersAreOldEnough = false
|
||||
break
|
||||
allUsersAreOldEnough = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log(allUsersAreOldEnough) // outputs false
|
||||
console.log(allUsersAreOldEnough); // outputs false
|
||||
```
|
||||
|
||||
## includes
|
||||
@@ -212,21 +212,21 @@ The `includes()` function tests if the passed item exists inside the array. It r
|
||||
|
||||
```js
|
||||
// given our data
|
||||
const pets = ["cat", "dog", "bat"]
|
||||
const pets = ["cat", "dog", "bat"];
|
||||
|
||||
// functional way
|
||||
const found = pets.includes("dog")
|
||||
const found = pets.includes("dog");
|
||||
|
||||
// iterative way
|
||||
let found = false
|
||||
let found = false;
|
||||
pets.forEach(pet => {
|
||||
if (pet === "dog") {
|
||||
found = true
|
||||
found = true;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// or most of the time done with indexOf
|
||||
const found = pets.indexOf("dog") > 0
|
||||
const found = pets.indexOf("dog") > 0;
|
||||
|
||||
console.log(found) // outputs true
|
||||
console.log(found); // outputs true
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ This basic example will return a promise with the value of `foo`.
|
||||
|
||||
## Await
|
||||
|
||||
The `await` keyword is used to wait until a promise has executed and fetches the result. In order to use the `await` keyword, you **need** to be inside an `async` function.
|
||||
The `await` keyword is used to wait until a promise has executed and fetches the result. In order to use the `await` keyword, you **need** to be inside an `async` function.
|
||||
|
||||
```js
|
||||
async function demo() {
|
||||
@@ -43,7 +43,7 @@ Another example to demonstrate this is if we extended the `foo()` function from
|
||||
```js
|
||||
async function foo() {
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
setTimeout(() => resolve("foo"), 5000)
|
||||
setTimeout(() => resolve("foo"), 5000);
|
||||
});
|
||||
|
||||
let data = await promise; // Execution waits here until promise resolves
|
||||
@@ -93,7 +93,7 @@ To get around this issue, you can use an declare asynchronous function anonymous
|
||||
})();
|
||||
```
|
||||
|
||||
Top-Level-Await is something that *may* get added to Javascript in the future, but for now wrapper functions like above are needed for this functionality.
|
||||
Top-Level-Await is something that _may_ get added to Javascript in the future, but for now wrapper functions like above are needed for this functionality.
|
||||
|
||||
## Real World Example
|
||||
|
||||
@@ -110,29 +110,33 @@ async function getCharacters() {
|
||||
(async () => {
|
||||
try {
|
||||
let characters = await getCharacters();
|
||||
console.log(characters) // (20) [{...}, {...}, {...}]
|
||||
console.log(characters); // (20) [{...}, {...}, {...}]
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
In this example, we are querying real data from the Rick & Morty API. This API has a lot of information regarding Rick & Morty, but here, we are trying to retrieve all the characters.
|
||||
In this example, we are querying real data from the Rick & Morty API. This API has a lot of information regarding Rick & Morty, but here, we are trying to retrieve all the characters.
|
||||
|
||||
- The first thing we do is use `fetch` to retrieve the data from the API. `fetch` is a built in function in web browsers to do http requests, and returns a Promise by default. We `await` the result of this.
|
||||
- When we get the result, we need to get the `JSON` value of the data. To do this, the `.json()` method from fetch is used. We then return the results.
|
||||
- As `await` can only be used in an `async` function, we use the method from above to make this work. As `getCharacters()` returns a Promise due to it being `async`, we `await` the result. We surround this in a `try...catch` in case fetch returns an error. Then, if no error is returned, `characters` contains the information we want, and is logged to the console. If `getCharacters()` returns an error, that error is also logged.
|
||||
|
||||
## Comparision with default Promises:
|
||||
|
||||
Below is the same code implemented without using Async / Await as a comparision. Here, you can see the differences between the two methods, and how Aysnc / Await makes the code appear in a *more synchronous* pattern, and can be clearer to follow.
|
||||
Below is the same code implemented without using Async / Await as a comparision. Here, you can see the differences between the two methods, and how Aysnc / Await makes the code appear in a _more synchronous_ pattern, and can be clearer to follow.
|
||||
|
||||
```js
|
||||
function getCharacters() {
|
||||
return fetch(`https://rickandmortyapi.com/api/character`)
|
||||
.then(response => response.json())
|
||||
.then(response => response.results)
|
||||
.then(response => response.results);
|
||||
}
|
||||
|
||||
getCharacters().then(characters => {
|
||||
console.log(characters) // (20) [{...}, {...}, {...}]
|
||||
}).catch(err => console.error(err))
|
||||
```
|
||||
getCharacters()
|
||||
.then(characters => {
|
||||
console.log(characters); // (20) [{...}, {...}, {...}]
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
```
|
||||
|
||||
@@ -31,8 +31,8 @@ called `getMembers` that retrieves all the members in a discord server. When we
|
||||
function we see the following result.
|
||||
|
||||
```js
|
||||
const members = getMembers("The Programmers Hangout")
|
||||
console.log(members) // Promise {<pending>}
|
||||
const members = getMembers("The Programmers Hangout");
|
||||
console.log(members); // Promise {<pending>}
|
||||
```
|
||||
|
||||
Normally, we would have expected to see an array of all the members but it takes time to
|
||||
@@ -44,8 +44,8 @@ to access the actual members like so.
|
||||
|
||||
```js
|
||||
getMembers("The Programmers Hangout").then(members => {
|
||||
console.log(members) // (32k) [{...}, {...}, {...}]
|
||||
})
|
||||
console.log(members); // (32k) [{...}, {...}, {...}]
|
||||
});
|
||||
```
|
||||
|
||||
This way we are able to make sure that we only try to `console.log` when the `getMembers` function has resolved and ready to be used.
|
||||
@@ -63,11 +63,11 @@ You may have tried doing something like this before.
|
||||
|
||||
```js
|
||||
// Incorrect code, don't copy!
|
||||
let results
|
||||
let results;
|
||||
getWeather("Los Angeles").then(weather => {
|
||||
results = weather
|
||||
})
|
||||
console.log(results) // undefined
|
||||
results = weather;
|
||||
});
|
||||
console.log(results); // undefined
|
||||
```
|
||||
|
||||
Why is `results` undefined? Because **Javascript doesn't wait**. Whenever a Promise is created,
|
||||
@@ -81,8 +81,8 @@ In order to fix this problem we need to move the `console.log` inside the `.then
|
||||
|
||||
```js
|
||||
getWeather("Los Angeles").then(weather => {
|
||||
console.log(weather) // Sunny, probably
|
||||
})
|
||||
console.log(weather); // Sunny, probably
|
||||
});
|
||||
```
|
||||
|
||||
### Real World Example
|
||||
@@ -94,12 +94,12 @@ You can try it in your browser if you want to test it out.
|
||||
function getCharacters() {
|
||||
return fetch(`https://rickandmortyapi.com/api/character`)
|
||||
.then(response => response.json())
|
||||
.then(response => response.results)
|
||||
.then(response => response.results);
|
||||
}
|
||||
|
||||
getCharacters().then(characters => {
|
||||
console.log(characters) // (20) [{...}, {...}, {...}]
|
||||
})
|
||||
console.log(characters); // (20) [{...}, {...}, {...}]
|
||||
});
|
||||
```
|
||||
|
||||
Let's break down what's happening in this function
|
||||
|
||||
@@ -14,10 +14,10 @@ function doAsync(number) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
doDatabase().then(function(dbResult) {
|
||||
otherDbFunction(dbResult).then(function(secondResult) {
|
||||
resolve(secondResult + 10)
|
||||
})
|
||||
})
|
||||
})
|
||||
resolve(secondResult + 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -28,9 +28,9 @@ already returns a promise, you can just return the original thing.
|
||||
function doAsync(number) {
|
||||
return doDatabase().then(function(dbResult) {
|
||||
otherDbFunction(dbResult).then(function(secondResult) {
|
||||
return secondResult + 10
|
||||
})
|
||||
})
|
||||
return secondResult + 10;
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,11 +41,11 @@ is that they allow you do chain them sequentially.
|
||||
function doAsync(number) {
|
||||
return doDatabase()
|
||||
.then(function(dbResult) {
|
||||
return otherDbFunction(dbResult)
|
||||
return otherDbFunction(dbResult);
|
||||
})
|
||||
.then(function(secondResult) {
|
||||
return secondResult + 10
|
||||
})
|
||||
return secondResult + 10;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,8 +57,8 @@ function doAsync(number) {
|
||||
return doDatabase()
|
||||
.then(otherDbFunction)
|
||||
.then(function(secondResult) {
|
||||
return secondResult + 10
|
||||
})
|
||||
return secondResult + 10;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -68,7 +68,7 @@ And you don't need those returns if you just have ES6 arrow functions
|
||||
const doAsync = number =>
|
||||
doDatabase()
|
||||
.then(otherDbFunction)
|
||||
.then(secondResult => secondResult + 10)
|
||||
.then(secondResult => secondResult + 10);
|
||||
```
|
||||
|
||||
Wow, that last one looks a lot cleaner to me than the first. Keeping that in mind, maybe we could be making some of our other functions cleaner as well
|
||||
|
||||
@@ -66,31 +66,31 @@ const b = [3, 4];
|
||||
console.log([...a, ...b]); // [1, 2, 3, 4] - Merged!
|
||||
```
|
||||
|
||||
Here's a real-world example. Imagine that you're making a function which accepts an ``options`` object as argument, but you also want to have default values for this object. Here's a not so good way to do it without spread operator:
|
||||
Here's a real-world example. Imagine that you're making a function which accepts an `options` object as argument, but you also want to have default values for this object. Here's a not so good way to do it without spread operator:
|
||||
|
||||
```js
|
||||
const f = (opts) => {
|
||||
const options = {};
|
||||
options.foo = opts.foo || "default value";
|
||||
options.bar = opts.bar || "default value";
|
||||
options.x = opts.x || 10;
|
||||
// ...
|
||||
}
|
||||
const f = opts => {
|
||||
const options = {};
|
||||
options.foo = opts.foo || "default value";
|
||||
options.bar = opts.bar || "default value";
|
||||
options.x = opts.x || 10;
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
This works, but here's a better way:
|
||||
|
||||
```js
|
||||
const f = (opts) => {
|
||||
const defaults = {
|
||||
foo: "default value",
|
||||
bar: "default value",
|
||||
x: 10
|
||||
};
|
||||
|
||||
const options = { ...defaults, ...opts };
|
||||
// ...
|
||||
}
|
||||
const f = opts => {
|
||||
const defaults = {
|
||||
foo: "default value",
|
||||
bar: "default value",
|
||||
x: 10,
|
||||
};
|
||||
|
||||
const options = { ...defaults, ...opts };
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
## Expanding an array as function arguments
|
||||
@@ -101,7 +101,8 @@ Spread operator also allows you to expand an array as function arguments. Each e
|
||||
const numbers = [2, 4, 8, 10, 11, 14];
|
||||
console.log(Math.max(...numbers)); // 14
|
||||
```
|
||||
The array gets expanded like so: ``Math.max(2, 4, 8, 10, 11, 14)``
|
||||
|
||||
The array gets expanded like so: `Math.max(2, 4, 8, 10, 11, 14)`
|
||||
|
||||
## Variadic functions
|
||||
|
||||
@@ -109,9 +110,10 @@ A variadic function is a function which accepts an arbitrary amount of arguments
|
||||
|
||||
```js
|
||||
const f = (...args) => {
|
||||
console.log(args);
|
||||
}
|
||||
console.log(args);
|
||||
};
|
||||
|
||||
f(1, 2, 3, 5); // [1, 2, 3, 5]
|
||||
```
|
||||
|
||||
As you can see, you can call the function with an infinite number of arguments and it will receive them as an array. Keep in mind that rest parameters must always be at the end.
|
||||
|
||||
@@ -23,13 +23,14 @@ If you do not have the Kotlin compiler installed on your computer you can downlo
|
||||
We will be creating a very simple library to demonstrate linking.
|
||||
|
||||
This is our `App.h` file
|
||||
|
||||
```c
|
||||
#ifndef APP_H
|
||||
#define APP_H
|
||||
|
||||
void run();
|
||||
void run();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
```
|
||||
|
||||
And this is our `App.c` file
|
||||
@@ -40,7 +41,7 @@ And this is our `App.c` file
|
||||
|
||||
void run() {
|
||||
printf("Hello, from C");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Compiling our C library
|
||||
@@ -49,7 +50,7 @@ First of all we will need to compile our C sources.
|
||||
|
||||
```bash
|
||||
gcc -c "-I$(pwd)" App.c -o App.o
|
||||
```
|
||||
```
|
||||
|
||||
The `"-I$(pwd)"` flag translates to the gcc -I flag with our current working directory as its parameter. You can also type out the full path if you want to.
|
||||
|
||||
@@ -59,14 +60,14 @@ We'll save our compiled static library in a file named `App.a`
|
||||
|
||||
```bash
|
||||
ar rcs App.a App.o
|
||||
```
|
||||
```
|
||||
|
||||
### Compiling our bindings
|
||||
|
||||
Now we need to create a `App.def` file for the Kotlin cinterop tool
|
||||
|
||||
|
||||
The minimal requirements for a `.def` file is some headers. We will include our header file here.
|
||||
|
||||
|
||||
```def
|
||||
headers = App.h
|
||||
```
|
||||
@@ -83,7 +84,7 @@ We're now ready to create a Kotlin file to interact with our C library so let's
|
||||
import App.run
|
||||
|
||||
fun main() {
|
||||
println("Hello, from Kotlin/Native")
|
||||
println("Hello, from Kotlin/Native")
|
||||
run()
|
||||
}
|
||||
```
|
||||
@@ -104,10 +105,11 @@ We can now run our executable so let's do that.
|
||||
|
||||
```bash
|
||||
./App.kexe
|
||||
```
|
||||
```
|
||||
|
||||
This should be the program output:
|
||||
|
||||
```text
|
||||
Hello, from Kotlin/Native
|
||||
Hello, from C
|
||||
Hello, from C
|
||||
```
|
||||
|
||||
@@ -38,6 +38,7 @@ This would fetch every single row from the `users` table as `1 = 1` will always
|
||||
You can think of a prepared statement as a conversation between the user and the database. A basic prepared statement would look a little like this:
|
||||
|
||||
> User: Hello Database, I'm going to run this prepared statement. I will tell you what it should look like, but I won't give you any values. Here's my query:
|
||||
|
||||
```sql
|
||||
SELECT * FROM `users` WHERE `age` > :age;
|
||||
```
|
||||
@@ -46,7 +47,7 @@ SELECT * FROM `users` WHERE `age` > :age;
|
||||
|
||||
> User: Here's the value for `:age`, it's the number `18`.
|
||||
|
||||
> Database: Okay, here are all the results where age was more than 18.
|
||||
> Database: Okay, here are all the results where age was more than 18.
|
||||
|
||||
Via a prepared statement we tell the database what our query will look like, then we pass the values. This means there is no way of modifying the SQL query and you'll probably just get a goofy result instead of having your entire database dropped.
|
||||
|
||||
@@ -110,7 +111,7 @@ Let's execute our SQL statement.
|
||||
$statement->execute();
|
||||
```
|
||||
|
||||
If you're looking to capture the result you can always do
|
||||
If you're looking to capture the result you can always do
|
||||
|
||||
```php
|
||||
$result = $statement->execute();
|
||||
|
||||
Reference in New Issue
Block a user