Исправления, автоклуб
This commit is contained in:
@@ -40,24 +40,28 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
]);
|
||||
}
|
||||
|
||||
handleTap() {
|
||||
// Если токен активирован, то открывается экран со сканером,
|
||||
// Если нет, то отправляется запрос на проверку статуса токена.
|
||||
handleTap() async {
|
||||
if (_tokenActive) {
|
||||
startScanner(context);
|
||||
} else {
|
||||
checkTokenStatus(token).then((response) {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
checkTokenStatus(token).then((response) {
|
||||
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
|
||||
// Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен.
|
||||
setState(() {
|
||||
_tokenActive = parsedMap['active'];
|
||||
// Обновить экран, заменить сообщение о необходимости активации токена, на сообщние о том, что токен активен.
|
||||
setState(() {
|
||||
_tokenActive = parsedMap['active'];
|
||||
});
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +96,7 @@ class _RegistrationScreenState extends BaseState<FinishRegistrationScreen> {
|
||||
|
||||
/// Получаем текст сообщения, в зависимости от статуса активации.
|
||||
getMessageString() {
|
||||
return _tokenActive ? 'Программа активирована' : 'Запрос на активацию программы отправлен, дождитесь подтверждения активации администратором';
|
||||
return _tokenActive ? tokenActiveMessage : tokenWaitMessage;
|
||||
}
|
||||
|
||||
/// Фоновое изображение для сообщения.
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Serious constants
|
||||
const String url = 'https://pos-api-int.dinect.com/20130701/';
|
||||
const String appToken = '9fec83cdca38c357e6b65dbb17514cdd36bf2a08';
|
||||
const String url = 'http://pos-api-autoclub.dinect.com/20130701/';
|
||||
const String appToken = 'bdea0f3ba9034b688019a7cac753d1209e2b227f';
|
||||
|
||||
// Hints
|
||||
// Texts
|
||||
const String merchantIDHint = 'ID магазина';
|
||||
const String posIDHint = 'Номер кассы';
|
||||
const String tokenActiveMessage = 'Программа активирована';
|
||||
const String tokenWaitMessage = 'Запрос на активацию программы отправлен, дождитесь подтверждения активации администратором';
|
||||
|
||||
// Assets
|
||||
const String logo_png = 'assets/registration_logo.png';
|
||||
|
||||
58
lib/faq.dart
58
lib/faq.dart
@@ -80,29 +80,49 @@ class FAQScreenState<T> extends BaseState<FAQScreen> {
|
||||
];
|
||||
|
||||
static const String registrationGuide = '''
|
||||
mPreviewing = true;После запуска приложения вы окажетесь на странице регистрации магазина.
|
||||
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
|
||||
Кликнете по кнопке: «Зарегистрировать»
|
||||
Дождитесь подтверждение активации программы, кликом по кнопке «Обновите статус активации» обновите статус.
|
||||
После подтверждения запроса на активацию программы Партнером/менеджером кликнете по кнопке «Завершить регистрацию», приложение готово к использованию.
|
||||
После запуска приложения вы окажетесь на странице регистрации магазина.
|
||||
|
||||
При желании изменить номер кассы, необходимо кликнуть на «значок» верхнем правом углу и вернуться на шаг регистрации.
|
||||
Введите DIN код магазина (выдается партнером/менеджером International Auto Club, дублируется на почту)
|
||||
|
||||
Кликните по кнопке: «Зарегистрировать»
|
||||
Дождитесь подтверждение активации программы, кликом по кнопке «Обновите статус активации» обновите статус.
|
||||
|
||||
После подтверждения запроса на активацию программы Партнером/менеджером кликните по кнопке «Завершить регистрацию», приложение готово к использованию.
|
||||
|
||||
При желании изменить номер кассы, необходимо кликнуть на «значок» верхнем правом углу и вернуться на шаг регистрации.
|
||||
''';
|
||||
|
||||
static const String usageGuide = '''Действие 1:
|
||||
При предъявлении покупателем штрих кода участника системы лояльности, запустите данное приложение.
|
||||
На экране появиться сканер штрих кодов. Поднесите гаджет к смартфону или карте покупателя и отсканируйте предъявленный штрих код сканером.
|
||||
При успешном сканировании на вашем экране появятся данные партнера.
|
||||
Действие 2:
|
||||
Необходимо ввести сумму покупки данного покупателя и нажать на кнопку «Зафиксировать».
|
||||
Всплывет окно “Подтверждения правильности ввода суммы” в случае правильного ввода суммы, нажмите “ДА”, сумма будет проведена и вознаграждение будет начислено участнику системы лояльности.
|
||||
static const String usageGuide = '''
|
||||
Действие 1:
|
||||
|
||||
При предъявлении покупателем штрих-кода участника системы лояльности, запустите данное приложение.
|
||||
|
||||
Если же сумма введена с ошибкой, нажмите “НЕТ” вы вернетесь на шаг ввода суммы и скорректируете ее.''';
|
||||
На экране появится сканер штрих кодов. Отсканируйте предъявленный штрих-код сканером.
|
||||
|
||||
static const String supportGuide = '''При некорректной работе приложения AUTO BONUS просьба сразу обратиться по телефону нашей технической поддержки: 8-800-234-6064 (звонок бесплатный), вас свяжут с менеджером. При звонке приготовьтесь назвать ИНН и наименование вашей организации.
|
||||
Рекомендуйте покупателям установить мобильное приложение дисконтной системы International Auto Club AUTO BONUS, и получайте кэш бэк и их покупок в любых наземных или интернет магазинов.
|
||||
Наш сайт https://www.auto-club.biz''';
|
||||
При успешном сканировании на вашем экране появятся данные партнера.
|
||||
|
||||
Действие 2:
|
||||
|
||||
Необходимо ввести сумму покупки данного покупателя и кликнуть по кнопке «Зафиксировать».
|
||||
|
||||
static const String commonGuide = '''Для эффективного считывания штрих кода участника системы лояльности необходимо камеру сканера поднести так, чтобы в неё не попадали вертикальные полосы рамки.
|
||||
Долгое сканирование происходит из-за черной рамки, в которую помещен штрих-код, вертикальные полосы этой рамки расцениваются сканером как часть штрих-кода.''';
|
||||
Всплывет окно подтверждения правильности ввода суммы». В случае правильного ввода суммы, кликните «ДА», сумма будет проведена и вознаграждение будет начислено участнику системы лояльности.
|
||||
|
||||
Если сумма введена с ошибкой, кликните «НЕТ» и Вы вернетесь на шаг ввода суммы и сможете её скорректировать.
|
||||
''';
|
||||
|
||||
static const String supportGuide = '''
|
||||
При некорректной работе приложения AUTO BONUS просьба сразу обратиться по телефону нашей технической поддержки: 8-800-234-6064 (звонок бесплатный) и Вас свяжут с менеджером.
|
||||
|
||||
При звонке приготовьтесь назвать ИНН и наименование вашей организации.
|
||||
|
||||
Рекомендуйте покупателям установить мобильное приложение дисконтной системы International Auto Club AUTO CLUB и получайте новых лояльных покупателей.
|
||||
|
||||
Наш сайт https://www.auto-club.biz
|
||||
''';
|
||||
|
||||
static const String commonGuide = '''
|
||||
Для эффективного считывания штрих-кода участника системы лояльности необходимо камеру сканера поднести так, чтобы в неё не попадали вертикальные полосы рамки.
|
||||
|
||||
Увеличение времени сканирования может произойти из-за черной рамки, в которую помещен штрих-код, так как вертикальные полосы этой рамки расцениваются сканером как часть штрих-кода.
|
||||
''';
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ class PurchaseScreen extends StatefulWidget {
|
||||
|
||||
class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
RegExp exp;
|
||||
RegExp moneyRegexp = new RegExp(r'''^(?!0\.00)\d{1,11}(\.\d{0,2})?$''');
|
||||
|
||||
PurchaseScreenState(String userString, String card) {
|
||||
this.user = JSON.decode(userString);
|
||||
this.card = card;
|
||||
exp = new RegExp(r'''^(?!0\.00)\d{1,11}(\.\d{0,2})?$''');
|
||||
getLoyality(user['loyalty_url']);
|
||||
}
|
||||
|
||||
@@ -40,15 +39,15 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
@override Widget getScreenContent() {
|
||||
return new Column(
|
||||
children: <Widget>[new Expanded(child: new ListView(children: <Widget>[
|
||||
getValueWithTitle('ФИО', user['first_name'] == null ? '' : user['first_name']),
|
||||
getValueWithTitle('Карта', card),
|
||||
getValueWithTitle('Вознаграждение', loyality),
|
||||
getHintLabel(),
|
||||
getDecoratedTextWidget(),
|
||||
buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => onPurchaseClick(context))),
|
||||
buildButton(new EdgeInsets.only(top: 24.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildFlatButton(context, 'СКАНИРОВАТЬ', primaryColor))
|
||||
]))]);
|
||||
children: <Widget>[new Expanded(child: new ListView(children: <Widget>[
|
||||
getValueWithTitle('ФИО', user['first_name'] == null ? '' : user['first_name']),
|
||||
getValueWithTitle('Карта', card),
|
||||
getValueWithTitle('Вознаграждение', loyality),
|
||||
getHintLabel(),
|
||||
getDecoratedTextWidget(),
|
||||
buildButton(new EdgeInsets.only(top: 36.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildRaisedButton(context, 'ЗАВЕРШИТЬ ПОКУПКУ', () => onPurchaseClick(context))),
|
||||
buildButton(new EdgeInsets.only(top: 24.0, left: buttonVerticalMargin, right: buttonVerticalMargin), buildFlatButton(context, 'СКАНИРОВАТЬ', primaryColor))
|
||||
]))]);
|
||||
}
|
||||
|
||||
@override String getTitle() {
|
||||
@@ -72,7 +71,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
setState(() {
|
||||
|
||||
if (tmpString.length == 0 || exp.hasMatch(tmpString)) {
|
||||
if (tmpString.length == 0 || moneyRegexp.hasMatch(tmpString)) {
|
||||
if (tmpString.contains('.')) {
|
||||
int dotIndex = tmpString.indexOf('.');
|
||||
integerPart = tmpString.substring(0, dotIndex);
|
||||
@@ -102,15 +101,14 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
onChanged: (text) => handleUserInput(text));
|
||||
}
|
||||
|
||||
getLoyality(String url) {
|
||||
getLoyality(String url) async {
|
||||
|
||||
print(url);
|
||||
print(token);
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken 9fec83cdca38c357e6b65dbb17514cdd36bf2a08',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken 9fec83cdca38c357e6b65dbb17514cdd36bf2a08',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
|
||||
httpClient.get(url, headers: headers).then((response) {
|
||||
|
||||
@@ -118,24 +116,18 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
|
||||
Map bonuses = JSON.decode(response.body);
|
||||
String type = bonuses['type'];
|
||||
|
||||
setState(() {
|
||||
if (type == 'amount') {
|
||||
this.loyality = '${user['discount']}%';
|
||||
} else {
|
||||
List amountToBonus = bonuses['amount_to_bonus'];
|
||||
// print(amountToBonus[0]);
|
||||
// print(amountToBonus[1]);
|
||||
this.loyality = '${(amountToBonus[0] / double.parse(amountToBonus[1])).toStringAsFixed(0)}%';
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_buildSum() {
|
||||
@@ -175,33 +167,36 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
|
||||
]));
|
||||
}
|
||||
|
||||
purchase(String sum_total) {
|
||||
purchase(String sum_total) async {
|
||||
|
||||
platform.invokeMethod('getDocID').then((result) {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
|
||||
String url = user['purchases_url'];
|
||||
platform.invokeMethod('getDocID').then((result) {
|
||||
|
||||
var body = {
|
||||
'doc_id': result,
|
||||
'curr_iso_code': '643',
|
||||
'commit': 'true',
|
||||
'sum_total': sum_total
|
||||
};
|
||||
String url = user['purchases_url'];
|
||||
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken 9fec83cdca38c357e6b65dbb17514cdd36bf2a08',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
var body = {
|
||||
'doc_id': result,
|
||||
'curr_iso_code': '643',
|
||||
'commit': 'true',
|
||||
'sum_total': sum_total
|
||||
};
|
||||
|
||||
httpClient.post(url, body: body, headers: headers).then((response) {
|
||||
|
||||
print(response.body);
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new PurchaseSuccessScreen(sum_total, user['first_name'] == null ? '' : user['first_name']));
|
||||
var headers = {
|
||||
'DM-Authorization': 'dmapptoken 9fec83cdca38c357e6b65dbb17514cdd36bf2a08',
|
||||
'Authorization': 'dmtoken ${token}'
|
||||
};
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
httpClient.post(url, body: body, headers: headers).then((response) {
|
||||
|
||||
print(response.body);
|
||||
Navigator.of(context).pop();
|
||||
pushRoute(context, new PurchaseSuccessScreen(sum_total, user['first_name'] == null ? '' : user['first_name']));
|
||||
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,19 +22,29 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
return 'ID магазина';
|
||||
}
|
||||
|
||||
/// Высота контейнера задана для того, чтобы элементы располагались вверху экрана
|
||||
/// и список скроллился снизу вверх при открытии клавиатуры.
|
||||
// Список виджетов, автоматически прокручиваемый вверх при открытии клавиатуры.
|
||||
@override Widget getScreenContent() {
|
||||
return new Container(
|
||||
child: new ListView(children: <Widget>[
|
||||
new Center(child: new Column(children: <Widget>[
|
||||
new Column(children: <Widget>[
|
||||
getLogo(),
|
||||
getHintLabel(),
|
||||
getDecoratedTextWidget(),
|
||||
new Container(margin: new EdgeInsets.only(top: 36.0), child: buildRaisedButton(context, 'ЗАРЕГИСТРИРОВАТЬ', _isValidMerchantID() && !loading ? () => _registerShop(context) : null))]))
|
||||
getButton()])
|
||||
]));
|
||||
}
|
||||
|
||||
// Возвращает кнопку регистрации.
|
||||
getButton() {
|
||||
return new Container(margin: new EdgeInsets.only(top: 36.0), child:
|
||||
buildRaisedButton(context, 'ЗАРЕГИСТРИРОВАТЬ', getOnPressed()));
|
||||
}
|
||||
|
||||
// Возвращает обработчик нажатий на кнопку регистрации.
|
||||
getOnPressed() {
|
||||
return _isValidMerchantID() && !loading ? () => _registerShop(context) : null;
|
||||
}
|
||||
|
||||
/// Токен кассы - это DIN код. DIN код - это специальный код динекта, максимальная его длина - 25 символов.
|
||||
_isValidMerchantID() {
|
||||
print("${textFieldValue.length}");
|
||||
@@ -51,28 +61,29 @@ class _RegistrationScreenState extends BaseState<RegistrationScreen> {
|
||||
|
||||
/// Получение от платформы id установки, формирование запроса на получение токена, сохранение токена.
|
||||
_register(BuildContext context) async {
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
createToken(textFieldValue, await platform.invokeMethod('getPosID')).then((response) {
|
||||
|
||||
createToken(textFieldValue, await platform.invokeMethod('getPosID')).then((response) {
|
||||
|
||||
setState(() {
|
||||
error = null;
|
||||
loading = false;
|
||||
});
|
||||
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
if (response.statusCode == 201) {
|
||||
token = parsedMap['token'];
|
||||
platform.invokeMethod('saveToken', {'token' : token});
|
||||
platform.invokeMethod('saveMerchantID', {'merchantID' : textFieldValue});
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
} else {
|
||||
setState(() {
|
||||
error = parsedMap['errors'][0];
|
||||
error = null;
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
|
||||
print(response.body);
|
||||
Map parsedMap = JSON.decode(response.body);
|
||||
if (response.statusCode == 201) {
|
||||
token = parsedMap['token'];
|
||||
platform.invokeMethod('saveToken', {'token' : token});
|
||||
platform.invokeMethod('saveMerchantID', {'merchantID' : textFieldValue});
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
} else {
|
||||
setState(() {
|
||||
error = parsedMap['errors'][0];
|
||||
});
|
||||
}
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,27 +18,28 @@ class SplashScreen extends StatelessWidget {
|
||||
showNextScreen(context);
|
||||
});
|
||||
|
||||
return new Stack(children: <Widget>[getBackgroundContainer(), getLogo(),
|
||||
return new Stack(children: <Widget>[getSplashBackground(), getLogo(),
|
||||
new Align(alignment: FractionalOffset.bottomRight, child:
|
||||
new Container(margin: new EdgeInsets.only(right: 11.0, bottom: 5.0), child: new Image.asset(powered_by_dinect_splash_png, height: 16.0, width: 122.0)))]);
|
||||
new Container(margin: new EdgeInsets.only(right: 11.0, bottom: 5.0), child:
|
||||
new Image.asset(powered_by_dinect_splash_png, height: 16.0, width: 122.0)))]);
|
||||
}
|
||||
|
||||
// Возвращает столбец с логотипом приложения и текстом под ним.
|
||||
// Столбец занимает не все доступное пространство, а необходимый минимум в центре экрана.
|
||||
getLogo() {
|
||||
return new Center(child: new Column(mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[new Image.asset(logo_png, height: 112.0, width: 252.0),
|
||||
new Image.asset(splash_text_png, height: 40.0, width: 240.0)]));
|
||||
}
|
||||
|
||||
getBackgroundContainer() {
|
||||
const margin = 48.0;
|
||||
return new Container(padding: new EdgeInsets.only(left: margin, right: margin), decoration: getSplashBackground());
|
||||
}
|
||||
|
||||
// Возвращает контейнер, который содержит decoration с фоновым изображением.
|
||||
getSplashBackground() {
|
||||
return new BoxDecoration(image: new DecorationImage(image: new ExactAssetImage(splash_png), fit: BoxFit.cover));
|
||||
return new Container(decoration:
|
||||
new BoxDecoration(image:
|
||||
new DecorationImage(image: new ExactAssetImage(splash_png), fit: BoxFit.cover)));
|
||||
}
|
||||
|
||||
/// Запуск следующего экрана приложения.
|
||||
// Запуск следующего экрана приложения.
|
||||
showNextScreen(BuildContext context) async {
|
||||
|
||||
token = await platform.invokeMethod('getToken');
|
||||
@@ -49,12 +50,14 @@ class SplashScreen extends StatelessWidget {
|
||||
if (token == null) {
|
||||
pushRoute(context, new RegistrationScreen());
|
||||
} else {
|
||||
checkTokenStatus(token).then((statusResponse) {
|
||||
handleStatusResponse(context, statusResponse);
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
checkTokenStatus(token).then((statusResponse) {
|
||||
handleStatusResponse(context, statusResponse);
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,25 +81,25 @@ class SplashScreen extends StatelessWidget {
|
||||
if (active) {
|
||||
startScanner(context);
|
||||
} else {
|
||||
createToken(await platform.invokeMethod('getMerchantID'), await platform.invokeMethod('getPosID')).then((response) {
|
||||
print('response.body: ${response.body}');
|
||||
|
||||
if (response.statusCode == 409) {
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
} else if (response.statusCode == 201) {
|
||||
platform.invokeMethod('removeKeys').then((result) {
|
||||
Map parsedMap = JSON.decode(result);
|
||||
String t = parsedMap['token'];
|
||||
deleteToken(t).then((response) {
|
||||
print(response.body);
|
||||
Navigator.of(context).pop(); // Убираем текущий route
|
||||
pushRoute(context, new RegistrationScreen()); // Запускаем регистрацию
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
if (await platform.invokeMethod('isOnline')) {
|
||||
createToken(await platform.invokeMethod('getMerchantID'), await platform.invokeMethod('getPosID')).then((response) {
|
||||
if (response.statusCode == 409) {
|
||||
pushRoute(context, new FinishRegistrationScreen());
|
||||
} else if (response.statusCode == 201) {
|
||||
platform.invokeMethod('removeKeys').then((result) {
|
||||
Map parsedMap = JSON.decode(result);
|
||||
String t = parsedMap['token'];
|
||||
deleteToken(t).then((response) {
|
||||
print(response.body);
|
||||
Navigator.of(context).pop(); // Убираем текущий route
|
||||
pushRoute(context, new RegistrationScreen()); // Запускаем регистрацию
|
||||
}).catchError((error) {
|
||||
print(error.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}).catchError((error) => print(error.toString()));
|
||||
}
|
||||
}).catchError((error) => print(error.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user