mirror of
https://github.com/haorendashu/nowser.git
synced 2026-02-23 10:24:24 +01:00
setting page, i18n
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nowser/const/app_type.dart';
|
||||
|
||||
import '../../generated/l10n.dart';
|
||||
import '../tag_component.dart';
|
||||
|
||||
class AppTypeComponent extends StatefulWidget {
|
||||
@@ -15,13 +16,15 @@ class AppTypeComponent extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AppTypeComponent extends State<AppTypeComponent> {
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String typeName = "WEB";
|
||||
String typeName = s.WEB;
|
||||
if (widget.appType == AppType.ANDROID_APP) {
|
||||
typeName = "Android";
|
||||
typeName = s.Android;
|
||||
} else if (widget.appType == AppType.REMOTE) {
|
||||
typeName = "Remote";
|
||||
typeName = s.Remote;
|
||||
}
|
||||
|
||||
return TagComponent(typeName);
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:nowser/util/router_util.dart';
|
||||
|
||||
import '../../const/base.dart';
|
||||
import '../../data/app.dart';
|
||||
import '../../generated/l10n.dart';
|
||||
|
||||
class AuthAppConnectDialog extends StatefulWidget {
|
||||
App app;
|
||||
@@ -37,10 +38,12 @@ class _AuthAppConnectDialog extends State<AuthAppConnectDialog> {
|
||||
|
||||
bool showDetail = false;
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
var baseMargin = EdgeInsets.only(
|
||||
var baseMargin = const EdgeInsets.only(
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
);
|
||||
@@ -52,24 +55,24 @@ class _AuthAppConnectDialog extends State<AuthAppConnectDialog> {
|
||||
value: ConnectType.FULLY_TRUST,
|
||||
groupValue: connectType,
|
||||
onChanged: onConnectTypeChange,
|
||||
title: Text("I fully trust it"),
|
||||
subtitle: Text("Auto-sign all requests (except payments)"),
|
||||
title: Text(s.Full_trust_title),
|
||||
subtitle: Text(s.Full_trust_des),
|
||||
));
|
||||
|
||||
list.add(RadioListTile(
|
||||
value: ConnectType.REASONABLE,
|
||||
groupValue: connectType,
|
||||
onChanged: onConnectTypeChange,
|
||||
title: Text("Let's be reasonable"),
|
||||
subtitle: Text("Auto-approve most common requests"),
|
||||
title: Text(s.Reasonable_title),
|
||||
subtitle: Text(s.Reasonable_des),
|
||||
));
|
||||
|
||||
list.add(RadioListTile(
|
||||
value: ConnectType.ALWAY_REJECT,
|
||||
groupValue: connectType,
|
||||
onChanged: onConnectTypeChange,
|
||||
title: Text("I'm a bit paranoid"),
|
||||
subtitle: Text("Do not sign anything without asking me!"),
|
||||
title: Text(s.Always_reject_title),
|
||||
subtitle: Text(s.Always_reject_des),
|
||||
));
|
||||
|
||||
var child = Column(
|
||||
@@ -79,7 +82,7 @@ class _AuthAppConnectDialog extends State<AuthAppConnectDialog> {
|
||||
|
||||
return AuthDialogBaseComponnet(
|
||||
app: widget.app,
|
||||
title: "App Connect",
|
||||
title: s.App_Connect,
|
||||
onConfirm: onConfirm,
|
||||
child: child,
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:nowser/main.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
|
||||
import '../../const/base.dart';
|
||||
import '../../generated/l10n.dart';
|
||||
|
||||
class AuthDialog extends StatefulWidget {
|
||||
App app;
|
||||
@@ -51,46 +52,49 @@ class _AuthDialog extends State<AuthDialog> {
|
||||
|
||||
bool always = false;
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
var baseMargin = EdgeInsets.only(
|
||||
var baseMargin = const EdgeInsets.only(
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
);
|
||||
var hintColor = themeData.hintColor;
|
||||
s = S.of(context);
|
||||
|
||||
var appName = widget.app.name;
|
||||
if (StringUtil.isNotBlank(widget.app.code)) {
|
||||
appName = widget.app.code;
|
||||
}
|
||||
// handle this title and des with widget.authType
|
||||
String authTitle = "Sign Event";
|
||||
String authDes = "Allow $appName to ";
|
||||
String authTitle = s.Sign_Event;
|
||||
String authDes = "${s.Allow} $appName ${s.to} ";
|
||||
if (widget.authType == AuthType.GET_PUBLIC_KEY) {
|
||||
authTitle = "Get Public Key";
|
||||
authDes += "get public key";
|
||||
authTitle = s.Get_Public_Key;
|
||||
authDes += s.Get_Public_Key;
|
||||
} else if (widget.authType == AuthType.SIGN_EVENT) {
|
||||
authTitle = "Sign Event";
|
||||
authDes += "sign a ${widget.eventKind} event";
|
||||
authTitle = s.Sign_Event;
|
||||
authDes += "${s.sign} ${s.a} ${widget.eventKind} ${s.event}";
|
||||
} else if (widget.authType == AuthType.GET_RELAYS) {
|
||||
authTitle = "Get Relays";
|
||||
authDes += "get relays";
|
||||
authTitle = s.Get_Relays;
|
||||
authDes += s.Get_Relays;
|
||||
} else if (widget.authType == AuthType.NIP04_ENCRYPT) {
|
||||
authTitle = "Encrypt (NIP-04)";
|
||||
authDes += "Encrypt (NIP-04)";
|
||||
authTitle = s.Encrypt04_name;
|
||||
authDes += s.Encrypt04_name;
|
||||
} else if (widget.authType == AuthType.NIP04_DECRYPT) {
|
||||
authTitle = "Decrypt (NIP-04)";
|
||||
authDes += "Decrypt (NIP-04)";
|
||||
authTitle = s.Decrypt04_name;
|
||||
authDes += s.Decrypt04_name;
|
||||
} else if (widget.authType == AuthType.NIP44_ENCRYPT) {
|
||||
authTitle = "Encrypt (NIP-44)";
|
||||
authDes += "Encrypt (NIP-44)";
|
||||
authTitle = s.Encrypt44_name;
|
||||
authDes += s.Encrypt44_name;
|
||||
} else if (widget.authType == AuthType.NIP44_DECRYPT) {
|
||||
authTitle = "Decrypt (NIP-44)";
|
||||
authDes += "Decrypt (NIP-44)";
|
||||
authTitle = s.Decrypt44_name;
|
||||
authDes += s.Decrypt44_name;
|
||||
} else if (widget.authType == AuthType.DECRYPT_ZAP_EVENT) {
|
||||
authTitle = "Decrypt zap event";
|
||||
authDes += "Decrypt zap event";
|
||||
authTitle = s.Decrypt_zap_event;
|
||||
authDes += s.Decrypt_zap_event;
|
||||
}
|
||||
|
||||
List<Widget> list = [];
|
||||
@@ -112,7 +116,7 @@ class _AuthDialog extends State<AuthDialog> {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("detail"),
|
||||
Text(s.detail),
|
||||
showDetail ? Icon(Icons.expand_less) : Icon(Icons.expand_more),
|
||||
],
|
||||
),
|
||||
@@ -165,7 +169,7 @@ class _AuthDialog extends State<AuthDialog> {
|
||||
});
|
||||
},
|
||||
),
|
||||
Text("Always"),
|
||||
Text(s.Always),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:nowser/provider/key_provider.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../generated/l10n.dart';
|
||||
import '../app/simple_app_info_component.dart';
|
||||
import '../logo_component.dart';
|
||||
|
||||
@@ -42,14 +43,17 @@ class AuthDialogBaseComponnet extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AuthDialog extends State<AuthDialogBaseComponnet> {
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
var baseMargin = EdgeInsets.only(
|
||||
var baseMargin = const EdgeInsets.only(
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
);
|
||||
var hintColor = themeData.hintColor;
|
||||
s = S.of(context);
|
||||
|
||||
List<Widget> list = [];
|
||||
|
||||
@@ -158,7 +162,7 @@ class _AuthDialog extends State<AuthDialogBaseComponnet> {
|
||||
RouterUtil.back(context);
|
||||
}
|
||||
},
|
||||
child: Text("Cancel"),
|
||||
child: Text(s.Cancel),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.red),
|
||||
),
|
||||
@@ -171,7 +175,7 @@ class _AuthDialog extends State<AuthDialogBaseComponnet> {
|
||||
onPressed: () {
|
||||
widget.onConfirm();
|
||||
},
|
||||
child: Text("Confirm"),
|
||||
child: Text(s.Confirm),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:nowser/data/bookmark_db.dart';
|
||||
import 'package:quick_actions/quick_actions.dart';
|
||||
|
||||
import '../const/base.dart';
|
||||
import '../generated/l10n.dart';
|
||||
import '../main.dart';
|
||||
import '../util/router_util.dart';
|
||||
import '../util/table_mode_util.dart';
|
||||
@@ -61,9 +62,12 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
s = S.of(context);
|
||||
|
||||
List<Widget> list = [];
|
||||
list.add(Container(
|
||||
@@ -71,7 +75,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
bottom: Base.BASE_PADDING,
|
||||
),
|
||||
child: Text(
|
||||
"Add bookmark",
|
||||
s.Add_bookmark,
|
||||
style: TextStyle(
|
||||
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -83,7 +87,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
child: TextField(
|
||||
controller: nameTextController,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Name",
|
||||
labelText: s.Name,
|
||||
),
|
||||
),
|
||||
));
|
||||
@@ -92,7 +96,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
child: TextField(
|
||||
controller: urlTextController,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Url",
|
||||
labelText: s.Url,
|
||||
),
|
||||
),
|
||||
));
|
||||
@@ -101,7 +105,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
margin: EdgeInsets.only(top: Base.BASE_PADDING_HALF),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("Add to index"),
|
||||
Text(s.Add_to_index),
|
||||
Expanded(
|
||||
child: Checkbox(
|
||||
value: addedToIndex,
|
||||
@@ -122,7 +126,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
list.add(Container(
|
||||
child: Row(
|
||||
children: [
|
||||
Text("Add to quick action"),
|
||||
Text(s.Add_to_quick_action),
|
||||
Expanded(
|
||||
child: Checkbox(
|
||||
value: addedToQa,
|
||||
@@ -146,7 +150,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
bottom: Base.BASE_PADDING,
|
||||
),
|
||||
width: double.infinity,
|
||||
child: FilledButton(onPressed: confirm, child: Text("Confirm")),
|
||||
child: FilledButton(onPressed: confirm, child: Text(s.Confirm)),
|
||||
));
|
||||
|
||||
Widget main = Container(
|
||||
@@ -187,7 +191,7 @@ class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||
|
||||
if (StringUtil.isBlank(bookmark.title) ||
|
||||
StringUtil.isBlank(bookmark.url)) {
|
||||
BotToast.showText(text: "Input can't be null");
|
||||
BotToast.showText(text: s.Input_can_not_be_null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
148
lib/component/enum_selector_component.dart
Normal file
148
lib/component/enum_selector_component.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../const/base.dart';
|
||||
import '../const/base_consts.dart';
|
||||
import '../util/router_util.dart';
|
||||
|
||||
class EnumSelectorComponent extends StatelessWidget {
|
||||
final List<EnumObj> list;
|
||||
|
||||
Widget Function(BuildContext, EnumObj)? enumItemBuild;
|
||||
|
||||
EnumSelectorComponent({
|
||||
required this.list,
|
||||
this.enumItemBuild,
|
||||
});
|
||||
|
||||
static Future<EnumObj?> show(BuildContext context, List<EnumObj> list) async {
|
||||
return await showDialog<EnumObj?>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (_context) {
|
||||
return EnumSelectorComponent(
|
||||
list: list,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
Color cardColor = themeData.cardColor;
|
||||
var maxHeight = MediaQuery.of(context).size.height;
|
||||
|
||||
List<Widget> widgets = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var enumObj = list[i];
|
||||
if (enumItemBuild != null) {
|
||||
widgets.add(enumItemBuild!(context, enumObj));
|
||||
} else {
|
||||
widgets.add(EnumSelectorItemComponent(
|
||||
enumObj: enumObj,
|
||||
isLast: i == list.length - 1,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Widget main = Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(
|
||||
left: Base.BASE_PADDING,
|
||||
right: Base.BASE_PADDING,
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(15)),
|
||||
color: cardColor,
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: maxHeight * 0.8,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: widgets,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black.withOpacity(0.2),
|
||||
body: FocusScope(
|
||||
// autofocus: true,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
RouterUtil.back(context);
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
padding: const EdgeInsets.only(
|
||||
left: Base.BASE_PADDING,
|
||||
right: Base.BASE_PADDING,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {},
|
||||
child: main,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumSelectorItemComponent extends StatelessWidget {
|
||||
static const double HEIGHT = 44;
|
||||
|
||||
final EnumObj enumObj;
|
||||
|
||||
final bool isLast;
|
||||
|
||||
Function(EnumObj)? onTap;
|
||||
|
||||
Color? color;
|
||||
|
||||
EnumSelectorItemComponent({
|
||||
required this.enumObj,
|
||||
this.isLast = false,
|
||||
this.onTap,
|
||||
this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
var dividerColor = themeData.dividerColor;
|
||||
|
||||
Widget main = Container(
|
||||
padding: const EdgeInsets.only(
|
||||
left: Base.BASE_PADDING + 5, right: Base.BASE_PADDING + 5),
|
||||
child: Text(enumObj.name),
|
||||
);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap!(enumObj);
|
||||
} else {
|
||||
RouterUtil.back(context, enumObj);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
border:
|
||||
isLast ? null : Border(bottom: BorderSide(color: dividerColor)),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
height: HEIGHT,
|
||||
child: main,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,15 @@ class _TextInputDialogInnerComponent
|
||||
controller = TextEditingController(text: widget.value);
|
||||
}
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
Color cardColor = themeData.cardColor;
|
||||
var mainColor = themeData.primaryColor;
|
||||
var titleFontSize = themeData.textTheme.bodyLarge!.fontSize;
|
||||
s = S.of(context);
|
||||
|
||||
List<Widget> list = [];
|
||||
|
||||
@@ -74,7 +77,7 @@ class _TextInputDialogInnerComponent
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: _onConfirm,
|
||||
child: Text("Confirm"),
|
||||
child: Text(s.Confirm),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:nowser/main.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
|
||||
import '../../const/base.dart';
|
||||
import '../../generated/l10n.dart';
|
||||
|
||||
class UserLoginDialog extends StatefulWidget {
|
||||
static Future<void> show(BuildContext context) async {
|
||||
@@ -27,15 +28,18 @@ class _UserLoginDialog extends State<UserLoginDialog> {
|
||||
|
||||
TextEditingController controller = TextEditingController();
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
s = S.of(context);
|
||||
|
||||
List<Widget> list = [];
|
||||
list.add(Container(
|
||||
margin: EdgeInsets.only(bottom: Base.BASE_PADDING * 2),
|
||||
child: Text(
|
||||
"Login",
|
||||
s.Login,
|
||||
style: TextStyle(
|
||||
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -74,7 +78,7 @@ class _UserLoginDialog extends State<UserLoginDialog> {
|
||||
list.add(Container(
|
||||
margin: EdgeInsets.only(bottom: Base.BASE_PADDING * 2),
|
||||
width: double.infinity,
|
||||
child: FilledButton(onPressed: confirm, child: Text("Confirm")),
|
||||
child: FilledButton(onPressed: confirm, child: Text(s.Confirm)),
|
||||
));
|
||||
|
||||
list.add(GestureDetector(
|
||||
@@ -84,7 +88,7 @@ class _UserLoginDialog extends State<UserLoginDialog> {
|
||||
},
|
||||
child: Container(
|
||||
child: Text(
|
||||
"Generate a private key",
|
||||
s.Generate_a_private_key,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
||||
@@ -64,33 +64,33 @@ class _WebViewComponent extends State<WebViewComponent>
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
contextMenu = ContextMenu(
|
||||
menuItems: [
|
||||
ContextMenuItem(
|
||||
id: 1,
|
||||
title: "Special",
|
||||
action: () async {
|
||||
print("Menu item Special clicked!");
|
||||
print(await webViewController?.getSelectedText());
|
||||
await webViewController?.clearFocus();
|
||||
})
|
||||
],
|
||||
settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: false),
|
||||
onCreateContextMenu: (hitTestResult) async {
|
||||
print("onCreateContextMenu");
|
||||
print(hitTestResult.extra);
|
||||
print(await webViewController?.getSelectedText());
|
||||
},
|
||||
onHideContextMenu: () {
|
||||
print("onHideContextMenu");
|
||||
},
|
||||
onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
||||
var id = contextMenuItemClicked.id;
|
||||
print("onContextMenuActionItemClicked: " +
|
||||
id.toString() +
|
||||
" " +
|
||||
contextMenuItemClicked.title);
|
||||
});
|
||||
// contextMenu = ContextMenu(
|
||||
// menuItems: [
|
||||
// ContextMenuItem(
|
||||
// id: 1,
|
||||
// title: "Special",
|
||||
// action: () async {
|
||||
// print("Menu item Special clicked!");
|
||||
// print(await webViewController?.getSelectedText());
|
||||
// await webViewController?.clearFocus();
|
||||
// })
|
||||
// ],
|
||||
// settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: false),
|
||||
// onCreateContextMenu: (hitTestResult) async {
|
||||
// print("onCreateContextMenu");
|
||||
// print(hitTestResult.extra);
|
||||
// print(await webViewController?.getSelectedText());
|
||||
// },
|
||||
// onHideContextMenu: () {
|
||||
// print("onHideContextMenu");
|
||||
// },
|
||||
// onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
||||
// var id = contextMenuItemClicked.id;
|
||||
// print("onContextMenuActionItemClicked: " +
|
||||
// id.toString() +
|
||||
// " " +
|
||||
// contextMenuItemClicked.title);
|
||||
// });
|
||||
|
||||
pullToRefreshController = kIsWeb ||
|
||||
![TargetPlatform.iOS, TargetPlatform.android]
|
||||
|
||||
Reference in New Issue
Block a user