From eb545f84b0e0cc57fead1ea35359d376ee2e5f0e Mon Sep 17 00:00:00 2001 From: otsmr Date: Fri, 18 Jul 2025 23:43:22 +0200 Subject: [PATCH] fix #220 --- .github/workflows/release_github.yml | 2 +- CHANGELOG.md | 2 +- lib/globals.dart | 1 + lib/src/localization/app_de.arb | 3 +- lib/src/localization/app_en.arb | 3 +- .../generated/app_localizations.dart | 6 + .../generated/app_localizations_de.dart | 4 + .../generated/app_localizations_en.dart | 4 + lib/src/model/json/userdata.dart | 5 +- lib/src/model/json/userdata.g.dart | 4 +- .../api/websocket/client_to_server.pb.dart | 14 ++ .../websocket/client_to_server.pbjson.dart | 9 +- .../protobuf/api/websocket/error.pbenum.dart | 2 + .../protobuf/api/websocket/error.pbjson.dart | 3 +- lib/src/providers/connection.provider.dart | 2 +- lib/src/services/api.service.dart | 11 +- lib/src/services/api/media_upload.dart | 3 - .../twonly_safe/restore.twonly_safe.dart | 5 + lib/src/utils/storage.dart | 3 +- lib/src/views/chats/add_new_user.view.dart | 90 ++++-------- lib/src/views/components/app_outdated.dart | 136 ++++++++++++------ 21 files changed, 190 insertions(+), 122 deletions(-) diff --git a/.github/workflows/release_github.yml b/.github/workflows/release_github.yml index 03a0e67..755c99a 100644 --- a/.github/workflows/release_github.yml +++ b/.github/workflows/release_github.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: {} push: branches: - - main + - main_disabled # paths: # - lib/** # - pubspec.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a691f..b7a6a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - twonly now has a free plan and is now financed by donations and an optional subscription with more features (coming soon) - iOS gestures to close images - Improved chat messages view, including better citation view and display times -- onboarding screens updated and registration view simplified +- Onboarding screens updated and registration view simplified - The sender is displayed in the top right corner when a media file is opened - Images are now stored as WebP to save storage - Button to report users diff --git a/lib/globals.dart b/lib/globals.dart index 297872e..b80e36a 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -19,6 +19,7 @@ void Function({required bool isConnected}) globalCallbackConnectionState = ({ required bool isConnected, }) {}; void Function() globalCallbackAppIsOutdated = () {}; +void Function() globalCallbackNewDeviceRegistered = () {}; void Function(String planId) globalCallbackUpdatePlan = (String planId) {}; bool globalIsAppInBackground = true; diff --git a/lib/src/localization/app_de.arb b/lib/src/localization/app_de.arb index eb507a7..88444e2 100644 --- a/lib/src/localization/app_de.arb +++ b/lib/src/localization/app_de.arb @@ -334,5 +334,6 @@ "openChangeLog": "Changelog automatisch öffnen", "reportUserTitle": "Melde {username}", "reportUserReason": "Meldegrund", - "reportUser": "Benutzer melden" + "reportUser": "Benutzer melden", + "newDeviceRegistered": "Du hast dich auf einem anderen Gerät angemeldet. Daher wurdest du hier abgemeldet." } \ No newline at end of file diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index ebfda7f..63ccda9 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -490,5 +490,6 @@ "openChangeLog": "Open changelog automatically", "reportUserTitle": "Report {username}", "reportUserReason": "Reporting reason", - "reportUser": "Report user" + "reportUser": "Report user", + "newDeviceRegistered": "You have logged in on another device. You have therefore been logged out here." } \ No newline at end of file diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index b3b8122..5f34009 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -2047,6 +2047,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Report user'** String get reportUser; + + /// No description provided for @newDeviceRegistered. + /// + /// In en, this message translates to: + /// **'You have logged in on another device. You have therefore been logged out here.'** + String get newDeviceRegistered; } class _AppLocalizationsDelegate diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index 8f2fb21..598a761 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -1087,4 +1087,8 @@ class AppLocalizationsDe extends AppLocalizations { @override String get reportUser => 'Benutzer melden'; + + @override + String get newDeviceRegistered => + 'Du hast dich auf einem anderen Gerät angemeldet. Daher wurdest du hier abgemeldet.'; } diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index be9c527..e199353 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -1081,4 +1081,8 @@ class AppLocalizationsEn extends AppLocalizations { @override String get reportUser => 'Report user'; + + @override + String get newDeviceRegistered => + 'You have logged in on another device. You have therefore been logged out here.'; } diff --git a/lib/src/model/json/userdata.dart b/lib/src/model/json/userdata.dart index bdf9a5c..702b340 100644 --- a/lib/src/model/json/userdata.dart +++ b/lib/src/model/json/userdata.dart @@ -29,9 +29,12 @@ class UserData { @JsonKey(defaultValue: 0) int avatarCounter = 0; + @JsonKey(defaultValue: 0) + int deviceId = 0; + // --- SUBSCRIPTION DTA --- - @JsonKey(defaultValue: 'Preview') + @JsonKey(defaultValue: 'Free') String subscriptionPlan; DateTime? lastImageSend; int? todaysImageCounter; diff --git a/lib/src/model/json/userdata.g.dart b/lib/src/model/json/userdata.g.dart index 6303414..3c615c0 100644 --- a/lib/src/model/json/userdata.g.dart +++ b/lib/src/model/json/userdata.g.dart @@ -10,12 +10,13 @@ UserData _$UserDataFromJson(Map json) => UserData( userId: (json['userId'] as num).toInt(), username: json['username'] as String, displayName: json['displayName'] as String, - subscriptionPlan: json['subscriptionPlan'] as String? ?? 'Preview', + subscriptionPlan: json['subscriptionPlan'] as String? ?? 'Free', isDemoUser: json['isDemoUser'] as bool? ?? false, ) ..avatarSvg = json['avatarSvg'] as String? ..avatarJson = json['avatarJson'] as String? ..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0 + ..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0 ..lastImageSend = json['lastImageSend'] == null ? null : DateTime.parse(json['lastImageSend'] as String) @@ -76,6 +77,7 @@ Map _$UserDataToJson(UserData instance) => { 'avatarSvg': instance.avatarSvg, 'avatarJson': instance.avatarJson, 'avatarCounter': instance.avatarCounter, + 'deviceId': instance.deviceId, 'subscriptionPlan': instance.subscriptionPlan, 'lastImageSend': instance.lastImageSend?.toIso8601String(), 'todaysImageCounter': instance.todaysImageCounter, diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart index dcec1c1..5d4ff99 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pb.dart @@ -445,6 +445,7 @@ class Handshake_Authenticate extends $pb.GeneratedMessage { $fixnum.Int64? userId, $core.List<$core.int>? authToken, $core.String? appVersion, + $fixnum.Int64? deviceId, }) { final $result = create(); if (userId != null) { @@ -456,6 +457,9 @@ class Handshake_Authenticate extends $pb.GeneratedMessage { if (appVersion != null) { $result.appVersion = appVersion; } + if (deviceId != null) { + $result.deviceId = deviceId; + } return $result; } Handshake_Authenticate._() : super(); @@ -466,6 +470,7 @@ class Handshake_Authenticate extends $pb.GeneratedMessage { ..aInt64(1, _omitFieldNames ? '' : 'userId') ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'authToken', $pb.PbFieldType.OY) ..aOS(3, _omitFieldNames ? '' : 'appVersion') + ..aInt64(4, _omitFieldNames ? '' : 'deviceId') ..hasRequiredFields = false ; @@ -516,6 +521,15 @@ class Handshake_Authenticate extends $pb.GeneratedMessage { $core.bool hasAppVersion() => $_has(2); @$pb.TagNumber(3) void clearAppVersion() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get deviceId => $_getI64(3); + @$pb.TagNumber(4) + set deviceId($fixnum.Int64 v) { $_setInt64(3, v); } + @$pb.TagNumber(4) + $core.bool hasDeviceId() => $_has(3); + @$pb.TagNumber(4) + void clearDeviceId() => clearField(4); } enum Handshake_Handshake { diff --git a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart index 284d527..7c05b1b 100644 --- a/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/client_to_server.pbjson.dart @@ -106,9 +106,11 @@ const Handshake_Authenticate$json = { {'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'}, {'1': 'auth_token', '3': 2, '4': 1, '5': 12, '10': 'authToken'}, {'1': 'app_version', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'appVersion', '17': true}, + {'1': 'device_id', '3': 4, '4': 1, '5': 3, '9': 1, '10': 'deviceId', '17': true}, ], '8': [ {'1': '_app_version'}, + {'1': '_device_id'}, ], }; @@ -128,9 +130,10 @@ final $typed_data.Uint8List handshakeDescriptor = $convert.base64Decode( 'lkGAcgASgDUg5yZWdpc3RyYXRpb25JZBIaCgZpc19pb3MYCCABKAhIAVIFaXNJb3OIAQFCDgoM' 'X2ludml0ZV9jb2RlQgkKB19pc19pb3MaEgoQR2V0QXV0aENoYWxsZW5nZRpDCgxHZXRBdXRoVG' '9rZW4SFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhoKCHJlc3BvbnNlGAIgASgMUghyZXNwb25z' - 'ZRp8CgxBdXRoZW50aWNhdGUSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEh0KCmF1dGhfdG9rZW' - '4YAiABKAxSCWF1dGhUb2tlbhIkCgthcHBfdmVyc2lvbhgDIAEoCUgAUgphcHBWZXJzaW9uiAEB' - 'Qg4KDF9hcHBfdmVyc2lvbkILCglIYW5kc2hha2U='); + 'ZRqsAQoMQXV0aGVudGljYXRlEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIdCgphdXRoX3Rva2' + 'VuGAIgASgMUglhdXRoVG9rZW4SJAoLYXBwX3ZlcnNpb24YAyABKAlIAFIKYXBwVmVyc2lvbogB' + 'ARIgCglkZXZpY2VfaWQYBCABKANIAVIIZGV2aWNlSWSIAQFCDgoMX2FwcF92ZXJzaW9uQgwKCl' + '9kZXZpY2VfaWRCCwoJSGFuZHNoYWtl'); @$core.Deprecated('Use applicationDataDescriptor instead') const ApplicationData$json = { diff --git a/lib/src/model/protobuf/api/websocket/error.pbenum.dart b/lib/src/model/protobuf/api/websocket/error.pbenum.dart index b2084c2..3e9c5e0 100644 --- a/lib/src/model/protobuf/api/websocket/error.pbenum.dart +++ b/lib/src/model/protobuf/api/websocket/error.pbenum.dart @@ -47,6 +47,7 @@ class ErrorCode extends $pb.ProtobufEnum { static const ErrorCode UserIdNotFound = ErrorCode._(1028, _omitEnumNames ? '' : 'UserIdNotFound'); static const ErrorCode UserIdAlreadyTaken = ErrorCode._(1029, _omitEnumNames ? '' : 'UserIdAlreadyTaken'); static const ErrorCode AppVersionOutdated = ErrorCode._(1030, _omitEnumNames ? '' : 'AppVersionOutdated'); + static const ErrorCode NewDeviceRegistered = ErrorCode._(1031, _omitEnumNames ? '' : 'NewDeviceRegistered'); static const $core.List values = [ Unknown, @@ -82,6 +83,7 @@ class ErrorCode extends $pb.ProtobufEnum { UserIdNotFound, UserIdAlreadyTaken, AppVersionOutdated, + NewDeviceRegistered, ]; static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values); diff --git a/lib/src/model/protobuf/api/websocket/error.pbjson.dart b/lib/src/model/protobuf/api/websocket/error.pbjson.dart index cd4bbd2..fad129d 100644 --- a/lib/src/model/protobuf/api/websocket/error.pbjson.dart +++ b/lib/src/model/protobuf/api/websocket/error.pbjson.dart @@ -50,6 +50,7 @@ const ErrorCode$json = { {'1': 'UserIdNotFound', '2': 1028}, {'1': 'UserIdAlreadyTaken', '2': 1029}, {'1': 'AppVersionOutdated', '2': 1030}, + {'1': 'NewDeviceRegistered', '2': 1031}, ], }; @@ -69,5 +70,5 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode( 'dlZBD+BxIVChBQbGFuTGltaXRSZWFjaGVkEP8HEhQKD05vdEVub3VnaENyZWRpdBCACBISCg1Q' 'bGFuRG93bmdyYWRlEIEIEhkKFFBsYW5VcGdyYWRlTm90WWVhcmx5EIIIEhgKE0ludmFsaWRTaW' 'duZWRQcmVLZXkQgwgSEwoOVXNlcklkTm90Rm91bmQQhAgSFwoSVXNlcklkQWxyZWFkeVRha2Vu' - 'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCA=='); + 'EIUIEhcKEkFwcFZlcnNpb25PdXRkYXRlZBCGCBIYChNOZXdEZXZpY2VSZWdpc3RlcmVkEIcI'); diff --git a/lib/src/providers/connection.provider.dart b/lib/src/providers/connection.provider.dart index 9d0941c..5b63969 100644 --- a/lib/src/providers/connection.provider.dart +++ b/lib/src/providers/connection.provider.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; class CustomChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { bool _isConnected = false; bool get isConnected => _isConnected; - String plan = 'Preview'; + String plan = 'Free'; Future updateConnectionState(bool update) async { _isConnected = update; notifyListeners(); diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index 52ddd9e..376ee0b 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -316,6 +316,13 @@ class ApiService { await close(() {}); return Result.error(ErrorCode.InternalError); } + if (res.error == ErrorCode.NewDeviceRegistered) { + globalCallbackNewDeviceRegistered(); + Log.error('Device is disabled, as a newer device restore twonly Safe.'); + appIsOutdated = true; + await close(() {}); + return Result.error(ErrorCode.InternalError); + } if (res.error == ErrorCode.SessionNotAuthenticated) { isAuthenticated = false; if (authenticated) { @@ -346,11 +353,13 @@ class ApiService { const storage = FlutterSecureStorage(); final apiAuthToken = await storage.read(key: SecureStorageKeys.apiAuthToken); + final user = await getUser(); - if (apiAuthToken != null) { + if (apiAuthToken != null && user != null) { final authenticate = Handshake_Authenticate() ..userId = Int64(userId) ..appVersion = (await PackageInfo.fromPlatform()).version + ..deviceId = Int64(user.deviceId) ..authToken = base64Decode(apiAuthToken); final handshake = Handshake()..authenticate = authenticate; diff --git a/lib/src/services/api/media_upload.dart b/lib/src/services/api/media_upload.dart index 5b2293b..e72c9e3 100644 --- a/lib/src/services/api/media_upload.dart +++ b/lib/src/services/api/media_upload.dart @@ -36,9 +36,6 @@ import 'package:video_compress/video_compress.dart'; Future isAllowedToSend() async { final user = await getUser(); if (user == null) return null; - if (user.subscriptionPlan == 'Preview') { - return ErrorCode.PlanNotAllowed; - } if (user.subscriptionPlan == 'Free') { var todaysImageCounter = user.todaysImageCounter; if (user.lastImageSend != null && user.todaysImageCounter != null) { diff --git a/lib/src/services/twonly_safe/restore.twonly_safe.dart b/lib/src/services/twonly_safe/restore.twonly_safe.dart index e6d4eaf..c925cb7 100644 --- a/lib/src/services/twonly_safe/restore.twonly_safe.dart +++ b/lib/src/services/twonly_safe/restore.twonly_safe.dart @@ -16,6 +16,7 @@ import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/protobuf/backup/backup.pb.dart'; import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/utils/storage.dart'; Future recoverTwonlySafe( String username, @@ -136,4 +137,8 @@ Future handleBackupData( await storage.write( key: SecureStorageKeys.userData, value: secureStorage[SecureStorageKeys.userData] as String); + await updateUserdata((u) { + u.deviceId += 1; + return u; + }); } diff --git a/lib/src/utils/storage.dart b/lib/src/utils/storage.dart index 052c584..b4b2e04 100644 --- a/lib/src/utils/storage.dart +++ b/lib/src/utils/storage.dart @@ -48,7 +48,8 @@ Future updateUsersPlan(BuildContext context, String planId) async { Mutex updateProtection = Mutex(); Future updateUserdata( - UserData Function(UserData userData) updateUser) async { + UserData Function(UserData userData) updateUser, +) async { return updateProtection.protect(() async { final user = await getUser(); if (user == null) return null; diff --git a/lib/src/views/chats/add_new_user.view.dart b/lib/src/views/chats/add_new_user.view.dart index 262ac2e..43ab8c8 100644 --- a/lib/src/views/chats/add_new_user.view.dart +++ b/lib/src/views/chats/add_new_user.view.dart @@ -6,7 +6,6 @@ import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/database/daos/contacts_dao.dart'; import 'package:twonly/src/database/tables/messages_table.dart'; @@ -14,7 +13,6 @@ import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'; import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart'; -import 'package:twonly/src/providers/connection.provider.dart'; import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/services/api/utils.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; @@ -24,7 +22,6 @@ import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/components/headline.dart'; import 'package:twonly/src/views/components/initialsavatar.dart'; -import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; class AddNewUserView extends StatefulWidget { const AddNewUserView({super.key}); @@ -143,7 +140,6 @@ class _SearchUsernameView extends State { @override Widget build(BuildContext context) { - final isPreview = context.read().plan == 'Preview'; return Scaffold( appBar: AppBar( title: Text(context.lang.searchUsernameTitle), @@ -154,49 +150,27 @@ class _SearchUsernameView extends State { const EdgeInsets.only(bottom: 20, left: 10, top: 20, right: 10), child: Column( children: [ - if (isPreview) ...[ - Padding( - padding: const EdgeInsets.all(20), - child: Text( - context.lang.searchUserNamePreview, - textAlign: TextAlign.center, - ), - ), - FilledButton.icon( - icon: const FaIcon(FontAwesomeIcons.shieldHeart), - onPressed: () { - Navigator.push(context, - MaterialPageRoute(builder: (context) { - return const SubscriptionView(); - })); + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: TextField( + onSubmitted: (_) { + _addNewUser(context); }, - label: Text(context.lang.selectSubscription), + onChanged: (value) { + searchUserName.text = value.toLowerCase(); + searchUserName.selection = TextSelection.fromPosition( + TextPosition(offset: searchUserName.text.length), + ); + }, + inputFormatters: [ + LengthLimitingTextInputFormatter(12), + FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z]')), + ], + controller: searchUserName, + decoration: + getInputDecoration(context.lang.searchUsernameInput), ), - const SizedBox(height: 30), - ], - if (!isPreview) ...[ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: TextField( - onSubmitted: (_) { - _addNewUser(context); - }, - onChanged: (value) { - searchUserName.text = value.toLowerCase(); - searchUserName.selection = TextSelection.fromPosition( - TextPosition(offset: searchUserName.text.length), - ); - }, - inputFormatters: [ - LengthLimitingTextInputFormatter(12), - FilteringTextInputFormatter.allow(RegExp('[a-z0-9A-Z]')), - ], - controller: searchUserName, - decoration: - getInputDecoration(context.lang.searchUsernameInput), - ), - ), - ], + ), const SizedBox(height: 20), if (contacts.isNotEmpty) HeadLineComponent( @@ -209,20 +183,18 @@ class _SearchUsernameView extends State { ), ), ), - floatingActionButton: isPreview - ? null - : Padding( - padding: const EdgeInsets.only(bottom: 30), - child: FloatingActionButton( - foregroundColor: Colors.white, - onPressed: () { - if (!_isLoading) _addNewUser(context); - }, - child: _isLoading - ? const Center(child: CircularProgressIndicator()) - : const FaIcon(FontAwesomeIcons.magnifyingGlassPlus), - ), - ), + floatingActionButton: Padding( + padding: const EdgeInsets.only(bottom: 30), + child: FloatingActionButton( + foregroundColor: Colors.white, + onPressed: () { + if (!_isLoading) _addNewUser(context); + }, + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : const FaIcon(FontAwesomeIcons.magnifyingGlassPlus), + ), + ), ); } } diff --git a/lib/src/views/components/app_outdated.dart b/lib/src/views/components/app_outdated.dart index 617144b..2dfca67 100644 --- a/lib/src/views/components/app_outdated.dart +++ b/lib/src/views/components/app_outdated.dart @@ -15,72 +15,114 @@ class AppOutdated extends StatefulWidget { class _AppOutdatedState extends State { bool appIsOutdated = false; + bool newDeviceRegistered = false; @override void dispose() { globalCallbackAppIsOutdated = () {}; + globalCallbackNewDeviceRegistered = () {}; super.dispose(); } - Future initAsync() async { + @override + void initState() { globalCallbackAppIsOutdated = () async { await context.read().updateConnectionState(false); setState(() { appIsOutdated = true; }); }; + globalCallbackNewDeviceRegistered = () async { + await context.read().updateConnectionState(false); + setState(() { + newDeviceRegistered = true; + }); + }; + super.initState(); } @override Widget build(BuildContext context) { - if (!appIsOutdated) return Container(); - return Positioned( - top: 60, - left: 30, - right: 30, - child: SafeArea( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), - decoration: BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.circular(10), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - context.lang.appOutdated, - textAlign: TextAlign.center, - softWrap: true, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(color: Colors.white, fontSize: 16), - ), - if (Platform.isAndroid) const SizedBox(height: 5), - if (Platform.isAndroid) - ElevatedButton( - onPressed: () { - launchUrl(Uri.parse( - 'https://play.google.com/store/apps/details?id=eu.twonly')); - }, - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - child: Text( - context.lang.appOutdatedBtn, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(color: Colors.white, fontSize: 16), - ), + if (newDeviceRegistered) { + return Positioned( + top: 60, + left: 30, + right: 30, + child: SafeArea( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), + decoration: BoxDecoration( + color: Colors.red.withAlpha(100), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + context.lang.newDeviceRegistered, + textAlign: TextAlign.center, + softWrap: true, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white, fontSize: 16), ), - ], + ], + ), ), ), - ), - ); + ); + } + if (appIsOutdated) { + return Positioned( + top: 60, + left: 30, + right: 30, + child: SafeArea( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + context.lang.appOutdated, + textAlign: TextAlign.center, + softWrap: true, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white, fontSize: 16), + ), + if (Platform.isAndroid) const SizedBox(height: 5), + if (Platform.isAndroid) + ElevatedButton( + onPressed: () { + launchUrl(Uri.parse( + 'https://play.google.com/store/apps/details?id=eu.twonly')); + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + context.lang.appOutdatedBtn, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white, fontSize: 16), + ), + ), + ], + ), + ), + ), + ); + } + return Container(); } }