dialog.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
  5. import 'package:flutter_hbb/common/widgets/toolbar.dart';
  6. import 'package:get/get.dart';
  7. import '../../common.dart';
  8. import '../../models/platform_model.dart';
  9. void _showSuccess() {
  10. showToast(translate("Successful"));
  11. }
  12. void _showError() {
  13. showToast(translate("Error"));
  14. }
  15. void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
  16. final pw = await bind.mainGetPermanentPassword();
  17. final p0 = TextEditingController(text: pw);
  18. final p1 = TextEditingController(text: pw);
  19. var validateLength = false;
  20. var validateSame = false;
  21. dialogManager.show((setState, close, context) {
  22. submit() async {
  23. close();
  24. dialogManager.showLoading(translate("Waiting"));
  25. if (await gFFI.serverModel.setPermanentPassword(p0.text)) {
  26. dialogManager.dismissAll();
  27. _showSuccess();
  28. } else {
  29. dialogManager.dismissAll();
  30. _showError();
  31. }
  32. }
  33. return CustomAlertDialog(
  34. title: Column(
  35. mainAxisAlignment: MainAxisAlignment.center,
  36. children: [
  37. Icon(Icons.password_rounded, color: MyTheme.accent),
  38. Text(translate('Set your own password')).paddingOnly(left: 10),
  39. ],
  40. ),
  41. content: Form(
  42. autovalidateMode: AutovalidateMode.onUserInteraction,
  43. child: Column(mainAxisSize: MainAxisSize.min, children: [
  44. TextFormField(
  45. autofocus: true,
  46. obscureText: true,
  47. keyboardType: TextInputType.visiblePassword,
  48. decoration: InputDecoration(
  49. labelText: translate('Password'),
  50. ),
  51. controller: p0,
  52. validator: (v) {
  53. if (v == null) return null;
  54. final val = v.trim().length > 5;
  55. if (validateLength != val) {
  56. // use delay to make setState success
  57. Future.delayed(Duration(microseconds: 1),
  58. () => setState(() => validateLength = val));
  59. }
  60. return val
  61. ? null
  62. : translate('Too short, at least 6 characters.');
  63. },
  64. ).workaroundFreezeLinuxMint(),
  65. TextFormField(
  66. obscureText: true,
  67. keyboardType: TextInputType.visiblePassword,
  68. decoration: InputDecoration(
  69. labelText: translate('Confirmation'),
  70. ),
  71. controller: p1,
  72. validator: (v) {
  73. if (v == null) return null;
  74. final val = p0.text == v;
  75. if (validateSame != val) {
  76. Future.delayed(Duration(microseconds: 1),
  77. () => setState(() => validateSame = val));
  78. }
  79. return val
  80. ? null
  81. : translate('The confirmation is not identical.');
  82. },
  83. ).workaroundFreezeLinuxMint(),
  84. ])),
  85. onCancel: close,
  86. onSubmit: (validateLength && validateSame) ? submit : null,
  87. actions: [
  88. dialogButton(
  89. 'Cancel',
  90. icon: Icon(Icons.close_rounded),
  91. onPressed: close,
  92. isOutline: true,
  93. ),
  94. dialogButton(
  95. 'OK',
  96. icon: Icon(Icons.done_rounded),
  97. onPressed: (validateLength && validateSame) ? submit : null,
  98. ),
  99. ],
  100. );
  101. });
  102. }
  103. void setTemporaryPasswordLengthDialog(
  104. OverlayDialogManager dialogManager) async {
  105. List<String> lengths = ['6', '8', '10'];
  106. String length = await bind.mainGetOption(key: "temporary-password-length");
  107. var index = lengths.indexOf(length);
  108. if (index < 0) index = 0;
  109. length = lengths[index];
  110. dialogManager.show((setState, close, context) {
  111. setLength(newValue) {
  112. final oldValue = length;
  113. if (oldValue == newValue) return;
  114. setState(() {
  115. length = newValue;
  116. });
  117. bind.mainSetOption(key: "temporary-password-length", value: newValue);
  118. bind.mainUpdateTemporaryPassword();
  119. Future.delayed(Duration(milliseconds: 200), () {
  120. close();
  121. _showSuccess();
  122. });
  123. }
  124. return CustomAlertDialog(
  125. title: Text(translate("Set one-time password length")),
  126. content: Row(
  127. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  128. children: lengths
  129. .map(
  130. (value) => Row(
  131. children: [
  132. Text(value),
  133. Radio(
  134. value: value, groupValue: length, onChanged: setLength),
  135. ],
  136. ),
  137. )
  138. .toList()),
  139. );
  140. }, backDismiss: true, clickMaskDismiss: true);
  141. }
  142. void showServerSettings(OverlayDialogManager dialogManager) async {
  143. Map<String, dynamic> options = {};
  144. try {
  145. options = jsonDecode(await bind.mainGetOptions());
  146. } catch (e) {
  147. print("Invalid server config: $e");
  148. }
  149. showServerSettingsWithValue(ServerConfig.fromOptions(options), dialogManager);
  150. }
  151. void showServerSettingsWithValue(
  152. ServerConfig serverConfig, OverlayDialogManager dialogManager) async {
  153. var isInProgress = false;
  154. final idCtrl = TextEditingController(text: serverConfig.idServer);
  155. final relayCtrl = TextEditingController(text: serverConfig.relayServer);
  156. final apiCtrl = TextEditingController(text: serverConfig.apiServer);
  157. final keyCtrl = TextEditingController(text: serverConfig.key);
  158. RxString idServerMsg = ''.obs;
  159. RxString relayServerMsg = ''.obs;
  160. RxString apiServerMsg = ''.obs;
  161. final controllers = [idCtrl, relayCtrl, apiCtrl, keyCtrl];
  162. final errMsgs = [
  163. idServerMsg,
  164. relayServerMsg,
  165. apiServerMsg,
  166. ];
  167. dialogManager.show((setState, close, context) {
  168. Future<bool> submit() async {
  169. setState(() {
  170. isInProgress = true;
  171. });
  172. bool ret = await setServerConfig(
  173. null,
  174. errMsgs,
  175. ServerConfig(
  176. idServer: idCtrl.text.trim(),
  177. relayServer: relayCtrl.text.trim(),
  178. apiServer: apiCtrl.text.trim(),
  179. key: keyCtrl.text.trim()));
  180. setState(() {
  181. isInProgress = false;
  182. });
  183. return ret;
  184. }
  185. Widget buildField(
  186. String label, TextEditingController controller, String errorMsg,
  187. {String? Function(String?)? validator, bool autofocus = false}) {
  188. if (isDesktop || isWeb) {
  189. return Row(
  190. children: [
  191. SizedBox(
  192. width: 120,
  193. child: Text(label),
  194. ),
  195. SizedBox(width: 8),
  196. Expanded(
  197. child: TextFormField(
  198. controller: controller,
  199. decoration: InputDecoration(
  200. errorText: errorMsg.isEmpty ? null : errorMsg,
  201. contentPadding:
  202. EdgeInsets.symmetric(horizontal: 8, vertical: 12),
  203. ),
  204. validator: validator,
  205. autofocus: autofocus,
  206. ).workaroundFreezeLinuxMint(),
  207. ),
  208. ],
  209. );
  210. }
  211. return TextFormField(
  212. controller: controller,
  213. decoration: InputDecoration(
  214. labelText: label,
  215. errorText: errorMsg.isEmpty ? null : errorMsg,
  216. ),
  217. validator: validator,
  218. ).workaroundFreezeLinuxMint();
  219. }
  220. return CustomAlertDialog(
  221. title: Row(
  222. children: [
  223. Expanded(child: Text(translate('ID/Relay Server'))),
  224. ...ServerConfigImportExportWidgets(controllers, errMsgs),
  225. ],
  226. ),
  227. content: ConstrainedBox(
  228. constraints: const BoxConstraints(minWidth: 500),
  229. child: Form(
  230. child: Obx(() => Column(
  231. mainAxisSize: MainAxisSize.min,
  232. children: [
  233. buildField(translate('ID Server'), idCtrl, idServerMsg.value,
  234. autofocus: true),
  235. SizedBox(height: 8),
  236. if (!isIOS && !isWeb) ...[
  237. buildField(translate('Relay Server'), relayCtrl,
  238. relayServerMsg.value),
  239. SizedBox(height: 8),
  240. ],
  241. buildField(
  242. translate('API Server'),
  243. apiCtrl,
  244. apiServerMsg.value,
  245. validator: (v) {
  246. if (v != null && v.isNotEmpty) {
  247. if (!(v.startsWith('http://') ||
  248. v.startsWith("https://"))) {
  249. return translate("invalid_http");
  250. }
  251. }
  252. return null;
  253. },
  254. ),
  255. SizedBox(height: 8),
  256. buildField('Key', keyCtrl, ''),
  257. if (isInProgress)
  258. Padding(
  259. padding: EdgeInsets.only(top: 8),
  260. child: LinearProgressIndicator(),
  261. ),
  262. ],
  263. )),
  264. ),
  265. ),
  266. actions: [
  267. dialogButton('Cancel', onPressed: () {
  268. close();
  269. }, isOutline: true),
  270. dialogButton(
  271. 'OK',
  272. onPressed: () async {
  273. if (await submit()) {
  274. close();
  275. showToast(translate('Successful'));
  276. } else {
  277. showToast(translate('Failed'));
  278. }
  279. },
  280. ),
  281. ],
  282. );
  283. });
  284. }
  285. void setPrivacyModeDialog(
  286. OverlayDialogManager dialogManager,
  287. List<TToggleMenu> privacyModeList,
  288. RxString privacyModeState,
  289. ) async {
  290. dialogManager.dismissAll();
  291. dialogManager.show((setState, close, context) {
  292. return CustomAlertDialog(
  293. title: Text(translate('Privacy mode')),
  294. content: Column(
  295. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  296. children: privacyModeList
  297. .map((value) => CheckboxListTile(
  298. contentPadding: EdgeInsets.zero,
  299. visualDensity: VisualDensity.compact,
  300. title: value.child,
  301. value: value.value,
  302. onChanged: value.onChanged,
  303. ))
  304. .toList()),
  305. );
  306. }, backDismiss: true, clickMaskDismiss: true);
  307. }