From 17efb187f085c145a161c3a424b9141595c90180 Mon Sep 17 00:00:00 2001 From: atavism Date: Fri, 3 May 2024 11:11:10 -0700 Subject: [PATCH] Fix issues with report an issue page (#1064) * Fix issues with report an issue page * check nil user and add set proxy all call * check nil user and add set proxy all call * Start settings service * simplify websocket code for receiving messages * update comments, clean-ups * do not dispose controller. * remove mobile call for setProxyAll --------- Co-authored-by: Jigar-f --- assets/locales/en.po | 2 +- desktop/app/app.go | 6 ++ desktop/lib.go | 17 +++++- lib/account/report_issue.dart | 14 +---- lib/account/settings.dart | 59 +++++++++++++++++++ lib/common/session_model.dart | 84 ++++++++++++++-------------- lib/common/ui/custom/text_field.dart | 6 +- lib/ffi.dart | 4 ++ 8 files changed, 135 insertions(+), 57 deletions(-) diff --git a/assets/locales/en.po b/assets/locales/en.po index 1c035bb77..a56449300 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -191,7 +191,7 @@ msgid "other" msgstr "Other" msgid "issue_description" -msgstr "Issue Description" +msgstr "Describe your issue" msgid "check_for_updates" msgstr "Check for Updates" diff --git a/desktop/app/app.go b/desktop/app/app.go index e6de2ed11..9e45bddbf 100644 --- a/desktop/app/app.go +++ b/desktop/app/app.go @@ -331,6 +331,12 @@ func (app *App) beforeStart(listenAddr string) { app.Exit(nil) os.Exit(0) } + + if e := app.settings.StartService(app.ws); e != nil { + app.Exit(fmt.Errorf("unable to register settings service: %q", e)) + return + } + app.AddExitFunc("stopping loconf scanner", LoconfScanner(app.settings, app.configDir, 4*time.Hour, func() (bool, bool) { return app.IsProUser(context.Background()) }, func() string { return "/img/lantern_logo.png" diff --git a/desktop/lib.go b/desktop/lib.go index 6f0ec8502..6431ee066 100644 --- a/desktop/lib.go +++ b/desktop/lib.go @@ -238,12 +238,27 @@ func getUserData() (*protos.User, error) { return nil, err } user := resp.User - if user.Email != "" { + if user != nil && user.Email != "" { a.Settings().SetEmailAddress(user.Email) } return user, nil } +//export proxyAll +func proxyAll() *C.char { + proxyAll := a.Settings().GetProxyAll() + if proxyAll { + return C.CString("true") + } + return C.CString("false") +} + +//export setProxyAll +func setProxyAll(value *C.char) { + proxyAll, _ := strconv.ParseBool(C.GoString(value)) + go a.Settings().SetProxyAll(proxyAll) +} + // tryCacheUserData retrieves the latest user data for the given user. // It first checks the cache and if present returns the user data stored there func tryCacheUserData() (*protos.User, error) { diff --git a/lib/account/report_issue.dart b/lib/account/report_issue.dart index 860c177de..0f485e3e4 100644 --- a/lib/account/report_issue.dart +++ b/lib/account/report_issue.dart @@ -48,13 +48,7 @@ class _ReportIssueState extends State { return null; }); - @override - void dispose() { - emailController.dispose(); - issueController.dispose(); - descController.dispose(); - super.dispose(); - } + @override Widget build(BuildContext context) { @@ -166,6 +160,7 @@ class _ReportIssueState extends State { ? const EdgeInsetsDirectional.all(16.0) : const EdgeInsetsDirectional.all(8.0), hintText: 'issue_description'.i18n, + floatingLabelBehavior: FloatingLabelBehavior.always, autovalidateMode: AutovalidateMode.disabled, maxLines: 8, keyboardType: TextInputType.multiline, @@ -176,7 +171,7 @@ class _ReportIssueState extends State { ), const Spacer(), Tooltip( - message: AppKeys.sendReport, + message: isDesktop() ? '' : AppKeys.sendReport, child: Button( width: 200, disabled: isButtonDisabled(), @@ -202,9 +197,6 @@ class _ReportIssueState extends State { if (issueController.text.isEmpty) { return true; } - if (descController.text.isEmpty) { - return true; - } return false; } diff --git a/lib/account/settings.dart b/lib/account/settings.dart index dc6a474ea..4736bc04c 100644 --- a/lib/account/settings.dart +++ b/lib/account/settings.dart @@ -17,6 +17,15 @@ class Settings extends StatelessWidget { final packageInfo = PackageInfo.fromPlatform(); + void openInfoProxyAll(BuildContext context) { + CDialog.showInfo( + context, + title: 'proxy_all'.i18n, + description: 'description_proxy_all_dialog'.i18n, + iconPath: ImagePaths.key, + ); + } + void changeLanguage(BuildContext context) async => await context.pushRoute(Language()); @@ -142,6 +151,56 @@ class Settings extends StatelessWidget { ], ), ), + //* Proxy all + if (isDesktop()) + sessionModel.proxyAll( + (BuildContext context, bool proxyAll, Widget? child) => + ListItemFactory.settingsItem( + header: 'VPN'.i18n, + icon: ImagePaths.key, + content: CInkWell( + onTap: () => openInfoProxyAll(context), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + child: CText( + 'proxy_everything_is' + .i18n + .fill([proxyAll ? 'ON'.i18n : 'OFF'.i18n]), + softWrap: false, + style: tsSubtitle1.short, + ), + ), + const Padding( + padding: EdgeInsetsDirectional.only(start: 4.0), + child: CAssetImage( + key: ValueKey('proxy_all_icon'), + path: ImagePaths.info, + size: 12, + ), + ), + ], + ), + ), + trailingArray: [ + FlutterSwitch( + width: 44.0, + height: 24.0, + valueFontSize: 12.0, + padding: 2, + toggleSize: 18.0, + value: proxyAll, + activeColor: indicatorGreen, + inactiveColor: offSwitchColor, + onToggle: (bool newValue) { + sessionModel.setProxyAll(newValue); + }, + ), + ], + ), + ), ListItemFactory.settingsItem( header: 'about'.i18n, content: 'privacy_policy'.i18n, diff --git a/lib/common/session_model.dart b/lib/common/session_model.dart index 8d147c274..aad3552b2 100644 --- a/lib/common/session_model.dart +++ b/lib/common/session_model.dart @@ -66,7 +66,6 @@ class SessionModel extends Model { 'hasSucceedingProxy', false, ); - } if (Platform.isAndroid) { @@ -82,11 +81,17 @@ class SessionModel extends Model { late ValueNotifier proxyAvailable; late ValueNotifier country; - // wsMessageProp parses the given json, checks if it represents a pro user message and - // returns the value (if any) in the map for the given property. - String? wsMessageProp(Map json, String field) { - if (json["type"] != "pro") return null; - return json["message"][field]; + // listenWebsocket listens for websocket messages from the server. If a message matches the given message type, + // the onMessage callback is triggered with the given property value + void listenWebsocket(WebsocketImpl? websocket, String messageType, + property, void Function(T?) onMessage) { + if (websocket == null) return; + websocket.messageStream.listen( + (json) { + if (json["type"] == messageType) onMessage(json["message"][property]); + }, + onError: (error) => appLogger.i("websocket error: ${error.description}"), + ); } Widget proUser(ValueWidgetBuilder builder) { @@ -97,18 +102,10 @@ class SessionModel extends Model { return ffiValueBuilder( 'prouser', defaultValue: false, - onChanges: (setValue) { - if (websocket == null) return; - websocket.messageStream.listen( - (json) { - final userStatus = wsMessageProp(json, "userStatus"); - if (userStatus != null && userStatus.toString() == "active") - setValue(true); - }, - onError: (error) => - appLogger.i("websocket error: ${error.description}"), - ); - }, + onChanges: (setValue) => + listenWebsocket(websocket, "pro", "userStatus", (value) { + if (value != null && value.toString() == "active") setValue(true); + }), ffiProUser, builder: builder, ); @@ -199,17 +196,10 @@ class SessionModel extends Model { return ffiValueBuilder( 'lang', defaultValue: 'en', - onChanges: (setValue) { - if (websocket == null) return; - websocket.messageStream.listen( - (json) { - final language = wsMessageProp(json, "language"); - if (language != null && language != "") setValue(language); - }, - onError: (error) => - appLogger.i("websocket error: ${error.description}"), - ); - }, + onChanges: (setValue) => + listenWebsocket(websocket, "pro", "language", (value) { + if (value != null && value.toString() != "") setValue(value.toString()); + }), ffiLang, builder: builder, ); @@ -305,12 +295,11 @@ class SessionModel extends Model { ); } - Future setProxyAll(bool on) async { - unawaited( - methodChannel.invokeMethod('setProxyAll', { - 'on': on, - }), - ); + Future setProxyAll(bool isOn) async { + if (isDesktop()) { + return await compute(ffiSetProxyAll, isOn ? 'true' : 'false'); + } + throw Exception("Not supported on mobile"); } Future getCountryCode() async { @@ -366,9 +355,9 @@ class SessionModel extends Model { .invokeMethod('resendRecoveryCode', {}); } - void setSelectedTab(BuildContext context,String tab) { - Provider.of(context, listen: false).setCurrentIndex(tab); - + void setSelectedTab(BuildContext context, String tab) { + Provider.of(context, listen: false) + .setCurrentIndex(tab); } Widget shouldShowGoogleAds(ValueWidgetBuilder builder) { @@ -378,7 +367,6 @@ class SessionModel extends Model { ); } - Widget replicaAddr(ValueWidgetBuilder builder) { if (isMobile()) { return subscribedSingleValueBuilder( @@ -472,10 +460,9 @@ class SessionModel extends Model { } Future hasUpdatePlansOrBuy() async { - return compute(ffiHasPlanUpdateOrBuy,''); + return compute(ffiHasPlanUpdateOrBuy, ''); } - Plan planFromJson(Map item) { print("called plans $item"); final locale = Localization.locale; @@ -794,6 +781,20 @@ class SessionModel extends Model { ); } + Widget proxyAll(ValueWidgetBuilder builder) { + final websocket = WebsocketImpl.instance(); + return ffiValueBuilder( + 'proxyAll', + defaultValue: false, + onChanges: (setValue) => + listenWebsocket(websocket, "settings", "proxyAll", (value) { + if (value != null) setValue(value as bool); + }), + ffiProxyAll, + builder: builder, + ); + } + Future setSplitTunneling(bool on) async { unawaited( methodChannel.invokeMethod('setSplitTunneling', { @@ -833,5 +834,4 @@ class SessionModel extends Model { Future disableScreenShot() { return methodChannel.invokeMethod('disableScreenshot', {}); } - } diff --git a/lib/common/ui/custom/text_field.dart b/lib/common/ui/custom/text_field.dart index ef53b7a44..200837011 100644 --- a/lib/common/ui/custom/text_field.dart +++ b/lib/common/ui/custom/text_field.dart @@ -21,6 +21,7 @@ class CTextField extends StatefulWidget { late final TextInputAction? textInputAction; late final void Function(String value)? onFieldSubmitted; late final String? actionIconPath; + late final FloatingLabelBehavior? floatingLabelBehavior; late final int? maxLength; late final InputCounterWidgetBuilder? buildCounter; late final TextCapitalization? textCapitalization; @@ -50,6 +51,7 @@ class CTextField extends StatefulWidget { this.textInputAction, this.onFieldSubmitted, this.actionIconPath, + this.floatingLabelBehavior, this.maxLength, this.buildCounter, this.textCapitalization, @@ -118,7 +120,7 @@ class _CTextFieldState extends State { // thumbVisibility: true, trackVisibility: true, child: Tooltip( - message: widget.tooltipMessage ?? '', + message: isDesktop() ? '' : (widget.tooltipMessage ?? ''), child: TextFormField( key: fieldKey, autofocus: widget.autofocus!, @@ -164,7 +166,7 @@ class _CTextFieldState extends State { bottom: 8, )), isDense: true, - floatingLabelBehavior: FloatingLabelBehavior.never, + floatingLabelBehavior: widget.floatingLabelBehavior ?? FloatingLabelBehavior.never, // we handle floating labels using our custom method below labelText: (widget.label is String) ? widget.label : '', helperText: widget.helperText, diff --git a/lib/ffi.dart b/lib/ffi.dart index 3c82d8191..db351743a 100644 --- a/lib/ffi.dart +++ b/lib/ffi.dart @@ -17,6 +17,8 @@ void sysProxyOff() => _bindings.sysProxyOff(); void setLang(lang) => _bindings.setSelectLang(lang); +void ffiSetProxyAll(String isOn) => _bindings.setProxyAll(isOn.toPointerChar()); + String websocketAddr() => _bindings.websocketAddr().cast().toDartString(); void ffiExit() { @@ -30,6 +32,8 @@ Pointer ffiLang() => _bindings.lang().cast(); Pointer ffiPlayVersion() => _bindings.playVersion().cast(); +Pointer ffiProxyAll() => _bindings.proxyAll().cast(); + Pointer ffiStoreVersion() => _bindings.storeVersion().cast(); Pointer ffiHasSucceedingProxy() =>