нахуевертил
This commit is contained in:
+12
-4
@@ -32,6 +32,7 @@ class Api {
|
|||||||
final _stateController = StreamController<SessionState>.broadcast();
|
final _stateController = StreamController<SessionState>.broadcast();
|
||||||
final _sessionExpiredController =
|
final _sessionExpiredController =
|
||||||
StreamController<SessionExpiredException>.broadcast();
|
StreamController<SessionExpiredException>.broadcast();
|
||||||
|
final _handshakeSuccessController = StreamController<String>.broadcast();
|
||||||
Map<dynamic, dynamic>? _userAgent;
|
Map<dynamic, dynamic>? _userAgent;
|
||||||
|
|
||||||
Map<dynamic, dynamic>? get userAgent => _userAgent;
|
Map<dynamic, dynamic>? get userAgent => _userAgent;
|
||||||
@@ -44,6 +45,8 @@ class Api {
|
|||||||
Stream<SessionState> get stateStream => _stateController.stream;
|
Stream<SessionState> get stateStream => _stateController.stream;
|
||||||
Stream<SessionExpiredException> get sessionExpiredStream =>
|
Stream<SessionExpiredException> get sessionExpiredStream =>
|
||||||
_sessionExpiredController.stream;
|
_sessionExpiredController.stream;
|
||||||
|
Stream<String> get handshakeSuccessStream =>
|
||||||
|
_handshakeSuccessController.stream;
|
||||||
SessionState get state => _sessionState;
|
SessionState get state => _sessionState;
|
||||||
|
|
||||||
StreamSubscription<Uint8List>? _dataSubscription;
|
StreamSubscription<Uint8List>? _dataSubscription;
|
||||||
@@ -93,6 +96,12 @@ class Api {
|
|||||||
_setSessionState(SessionState.online);
|
_setSessionState(SessionState.online);
|
||||||
_startPinging();
|
_startPinging();
|
||||||
logger.i('Сессия онлайн, хэндшейк ок');
|
logger.i('Сессия онлайн, хэндшейк ок');
|
||||||
|
_handshakeSuccessController.add(
|
||||||
|
response.payload['device_name'] as String? ?? 'Unknown',
|
||||||
|
);
|
||||||
|
if (_onReconnectCallback != null) {
|
||||||
|
_onReconnectCallback!();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.e('Хэндшейк отклонён: ${response.payload}');
|
logger.e('Хэндшейк отклонён: ${response.payload}');
|
||||||
}
|
}
|
||||||
@@ -254,7 +263,8 @@ class Api {
|
|||||||
await for (final packet in _receiver.feed(data)) {
|
await for (final packet in _receiver.feed(data)) {
|
||||||
if (packet.isError &&
|
if (packet.isError &&
|
||||||
packet.payload is Map &&
|
packet.payload is Map &&
|
||||||
packet.payload['message'] == 'FAIL_LOGIN_TOKEN') {
|
(packet.payload['message'] == 'FAIL_LOGIN_TOKEN' ||
|
||||||
|
packet.payload['message'] == 'FAIL_WRONG_PASSWORD')) {
|
||||||
_sessionExpiredController.add(
|
_sessionExpiredController.add(
|
||||||
SessionExpiredException(messageFromErrorPayload(packet.payload)),
|
SessionExpiredException(messageFromErrorPayload(packet.payload)),
|
||||||
);
|
);
|
||||||
@@ -277,13 +287,11 @@ class Api {
|
|||||||
_socketStateSubscription = null;
|
_socketStateSubscription = null;
|
||||||
_receiver.reset();
|
_receiver.reset();
|
||||||
_dispatcher.clearPending();
|
_dispatcher.clearPending();
|
||||||
|
_handshakeSuccessController.add('disconnected');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> reconnectAndLogin() async {
|
Future<void> reconnectAndLogin() async {
|
||||||
await connect();
|
await connect();
|
||||||
if (_sessionState == SessionState.online && _onReconnectCallback != null) {
|
|
||||||
_onReconnectCallback!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Function()? _onReconnectCallback;
|
void Function()? _onReconnectCallback;
|
||||||
|
|||||||
@@ -1022,7 +1022,9 @@ class AccountModule {
|
|||||||
void _checkPacketError(Packet packet, String method) {
|
void _checkPacketError(Packet packet, String method) {
|
||||||
if (packet.isError) {
|
if (packet.isError) {
|
||||||
final payload = packet.payload;
|
final payload = packet.payload;
|
||||||
if (payload is Map && payload['message'] == 'FAIL_LOGIN_TOKEN') {
|
if (payload is Map &&
|
||||||
|
(payload['message'] == 'FAIL_LOGIN_TOKEN' ||
|
||||||
|
payload['message'] == 'FAIL_WRONG_PASSWORD')) {
|
||||||
throw SessionExpiredException(messageFromErrorPayload(payload));
|
throw SessionExpiredException(messageFromErrorPayload(payload));
|
||||||
}
|
}
|
||||||
throw PacketError(messageFromErrorPayload(payload));
|
throw PacketError(messageFromErrorPayload(payload));
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ class SessionExpiredException extends PacketError {
|
|||||||
|
|
||||||
String messageFromErrorPayload(dynamic payload) {
|
String messageFromErrorPayload(dynamic payload) {
|
||||||
if (payload is Map) {
|
if (payload is Map) {
|
||||||
|
final msg = payload['message'];
|
||||||
|
if (msg == 'FAIL_WRONG_PASSWORD' || msg == 'FAIL_LOGIN_TOKEN') {
|
||||||
|
return 'Ваш токен был отклонён сервером, хм... Попробуйте войти ещё раз.';
|
||||||
|
}
|
||||||
for (final key in ['localizedMessage', 'message', 'title']) {
|
for (final key in ['localizedMessage', 'message', 'title']) {
|
||||||
final v = payload[key];
|
final v = payload[key];
|
||||||
if (v is String && v.trim().isNotEmpty) return v.trim();
|
if (v is String && v.trim().isNotEmpty) return v.trim();
|
||||||
|
|||||||
@@ -199,7 +199,20 @@ class _ChatListScreenState extends State<ChatListScreen>
|
|||||||
|
|
||||||
Future<void> _reloadChatsAndFolders() async {
|
Future<void> _reloadChatsAndFolders() async {
|
||||||
final p = await AppDatabase.loadActiveProfile();
|
final p = await AppDatabase.loadActiveProfile();
|
||||||
if (p != null) {
|
if (p == null) {
|
||||||
|
_syncFolderChatScrollControllersForCount(1);
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_folders = [];
|
||||||
|
_selectedFolderId = null;
|
||||||
|
_foldersListKnown = null;
|
||||||
|
_isInitialLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
final chats = await ChatsModule.getChats(p.id);
|
final chats = await ChatsModule.getChats(p.id);
|
||||||
var folders = await FoldersModule.loadFolders(p.id);
|
var folders = await FoldersModule.loadFolders(p.id);
|
||||||
final foldersKnown = await FoldersModule.hasReceivedFoldersList(p.id);
|
final foldersKnown = await FoldersModule.hasReceivedFoldersList(p.id);
|
||||||
@@ -244,7 +257,7 @@ class _ChatListScreenState extends State<ChatListScreen>
|
|||||||
_jumpFolderPageToSelection();
|
_jumpFolderPageToSelection();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} catch (_) {
|
||||||
_syncFolderChatScrollControllersForCount(1);
|
_syncFolderChatScrollControllersForCount(1);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -253,6 +266,9 @@ class _ChatListScreenState extends State<ChatListScreen>
|
|||||||
_foldersListKnown = null;
|
_foldersListKnown = null;
|
||||||
_isInitialLoading = false;
|
_isInitialLoading = false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_jumpFolderPageToSelection();
|
_jumpFolderPageToSelection();
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
|
|
||||||
import '../../../core/config/device_presets.dart';
|
import '../../../core/config/device_presets.dart';
|
||||||
import '../../../core/storage/spoofing_service.dart';
|
import '../../../core/storage/spoofing_service.dart';
|
||||||
|
import '../../../core/storage/token_storage.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../auth/login_screen.dart';
|
||||||
|
|
||||||
enum SpoofingMethod { partial, full }
|
enum SpoofingMethod { partial, full }
|
||||||
|
|
||||||
@@ -127,19 +129,10 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
_deviceNameController.text =
|
_deviceNameController.text =
|
||||||
'${androidInfo.manufacturer} ${androidInfo.model}';
|
'${androidInfo.manufacturer} ${androidInfo.model}';
|
||||||
_osVersionController.text = 'Android ${androidInfo.version.release}';
|
_osVersionController.text = 'Android ${androidInfo.version.release}';
|
||||||
_selectedDeviceType = 'ANDROID';
|
|
||||||
_selectedArch = androidInfo.supportedAbis.isNotEmpty
|
_selectedArch = androidInfo.supportedAbis.isNotEmpty
|
||||||
? androidInfo.supportedAbis.first
|
? androidInfo.supportedAbis.first
|
||||||
: 'arm64-v8a';
|
: 'arm64-v8a';
|
||||||
_buildNumberController.text = '$_hardcodedBuildNumber';
|
_buildNumberController.text = '$_hardcodedBuildNumber';
|
||||||
} else if (Platform.isIOS) {
|
|
||||||
final iosInfo = await deviceInfo.iosInfo;
|
|
||||||
_deviceNameController.text = iosInfo.name;
|
|
||||||
_osVersionController.text =
|
|
||||||
'${iosInfo.systemName} ${iosInfo.systemVersion}';
|
|
||||||
_selectedDeviceType = 'IOS';
|
|
||||||
_selectedArch = 'arm64';
|
|
||||||
_buildNumberController.text = '$_hardcodedBuildNumber';
|
|
||||||
} else {
|
} else {
|
||||||
await _applyGeneratedData();
|
await _applyGeneratedData();
|
||||||
}
|
}
|
||||||
@@ -168,15 +161,7 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
_appVersionController.text = _hardcodedVersion;
|
_appVersionController.text = _hardcodedVersion;
|
||||||
_deviceIdController.text = _generateDeviceId();
|
_deviceIdController.text = _generateDeviceId();
|
||||||
|
|
||||||
_selectedDeviceType = preset.deviceType;
|
|
||||||
|
|
||||||
if (preset.deviceType == 'ANDROID') {
|
|
||||||
_selectedArch = 'arm64-v8a';
|
_selectedArch = 'arm64-v8a';
|
||||||
} else if (preset.deviceType == 'IOS') {
|
|
||||||
_selectedArch = 'arm64';
|
|
||||||
} else {
|
|
||||||
_selectedArch = 'x86_64';
|
|
||||||
}
|
|
||||||
_buildNumberController.text = '$_hardcodedBuildNumber';
|
_buildNumberController.text = '$_hardcodedBuildNumber';
|
||||||
|
|
||||||
if (_selectedMethod == SpoofingMethod.full) {
|
if (_selectedMethod == SpoofingMethod.full) {
|
||||||
@@ -247,32 +232,76 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text(l10n.spoofDialogApplyTitle),
|
title: Text(l10n.spoofDialogApplyTitle),
|
||||||
content: Text(l10n.spoofDialogApplyContent),
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(l10n.spoofDialogApplyContent),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
l10n.spoofDialogApplyWarning,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
onPressed: () => Navigator.of(context).pop('cancel'),
|
||||||
child: Text(l10n.spoofDialogApplyDeny),
|
child: Text(l10n.spoofDialogApplyDeny),
|
||||||
),
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop('relogin'),
|
||||||
|
child: Text(l10n.spoofDialogReloginConfirm),
|
||||||
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
onPressed: () => Navigator.of(context).pop('apply'),
|
||||||
child: Text(l10n.spoofDialogApplyConfirm),
|
child: Text(l10n.spoofDialogApplyConfirm),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (confirmed != true || !mounted) return;
|
if (!mounted || confirmed == null) return;
|
||||||
|
|
||||||
|
if (confirmed == 'relogin') {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await _saveAllData(prefs);
|
||||||
|
await api.disconnect();
|
||||||
|
final accountId = await TokenStorage.getActiveAccountId();
|
||||||
|
if (accountId != null) {
|
||||||
|
await TokenStorage.deleteToken(accountId);
|
||||||
|
}
|
||||||
|
await prefs.setBool('spoofing_enabled', true);
|
||||||
|
await api.connect();
|
||||||
|
if (mounted) {
|
||||||
|
final navState = KometApp.navigatorKey.currentState;
|
||||||
|
if (navState != null) {
|
||||||
|
await navState.pushAndRemoveUntil(
|
||||||
|
MaterialPageRoute(builder: (_) => const LoginScreen()),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirmed != 'apply') return;
|
||||||
|
|
||||||
await _saveAllData(prefs);
|
await _saveAllData(prefs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.disconnect();
|
await api.disconnect();
|
||||||
await api.connect();
|
await api.connect();
|
||||||
if (mounted) Navigator.of(context).pop();
|
if (mounted && Navigator.of(context).canPop()) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -452,33 +481,31 @@ class _SpoofScreenState extends State<SpoofScreen> {
|
|||||||
text: l10n.spoofDeviceTypeDescription,
|
text: l10n.spoofDeviceTypeDescription,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_buildChipSelector<String>(
|
Row(
|
||||||
options: const [
|
children: [
|
||||||
_ChipOption('ANDROID', 'ANDROID', Icons.android_outlined),
|
_buildDisabledChip('ANDROID', Icons.android_outlined, theme),
|
||||||
_ChipOption('IOS', 'iOS', Icons.phone_iphone_outlined),
|
const SizedBox(width: 8),
|
||||||
_ChipOption(
|
_buildDisabledChip('iOS', Icons.phone_iphone_outlined, theme),
|
||||||
'DESKTOP',
|
const SizedBox(width: 8),
|
||||||
|
_buildDisabledChip(
|
||||||
'Desktop',
|
'Desktop',
|
||||||
Icons.desktop_windows_outlined,
|
Icons.desktop_windows_outlined,
|
||||||
|
theme,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
selected: _selectedDeviceType,
|
),
|
||||||
onSelected: (value) {
|
],
|
||||||
setState(() {
|
),
|
||||||
_selectedDeviceType = value;
|
),
|
||||||
if (value == 'ANDROID') {
|
);
|
||||||
_selectedArch = 'arm64-v8a';
|
|
||||||
} else if (value == 'IOS') {
|
|
||||||
_selectedArch = 'arm64';
|
|
||||||
} else {
|
|
||||||
_selectedArch = 'x86_64';
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
Widget _buildDisabledChip(String label, IconData icon, ThemeData theme) {
|
||||||
),
|
return Chip(
|
||||||
],
|
label: Text(label),
|
||||||
),
|
avatar: Icon(icon, size: 18, color: theme.colorScheme.onSurfaceVariant),
|
||||||
),
|
backgroundColor: theme.colorScheme.surfaceContainerHighest,
|
||||||
|
side: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -64,7 +64,7 @@
|
|||||||
"spoofMethodPartialDescription": "Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.",
|
"spoofMethodPartialDescription": "Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.",
|
||||||
"spoofMethodFullDescription": "All data including timezone and locale is generated randomly. Use this method at your own risk!",
|
"spoofMethodFullDescription": "All data including timezone and locale is generated randomly. Use this method at your own risk!",
|
||||||
"spoofDeviceTypeTitle": "Device type",
|
"spoofDeviceTypeTitle": "Device type",
|
||||||
"spoofDeviceTypeDescription": "Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.",
|
"spoofDeviceTypeDescription": "This field is not changeable, to avoid token association issues",
|
||||||
"spoofDeviceTypeLabel": "Device type",
|
"spoofDeviceTypeLabel": "Device type",
|
||||||
"spoofMainSectionTitle": "Main data",
|
"spoofMainSectionTitle": "Main data",
|
||||||
"spoofFieldDeviceName": "Device name",
|
"spoofFieldDeviceName": "Device name",
|
||||||
@@ -88,6 +88,12 @@
|
|||||||
"spoofDialogYes": "Yes",
|
"spoofDialogYes": "Yes",
|
||||||
"spoofDialogApplyTitle": "Apply settings?",
|
"spoofDialogApplyTitle": "Apply settings?",
|
||||||
"spoofDialogApplyContent": "Need to reconnect the app, ok?",
|
"spoofDialogApplyContent": "Need to reconnect the app, ok?",
|
||||||
|
"spoofDialogApplyWarning": "Your spoof will change immediately. But due to MAX specifics, you must re-login to the account for it to become visible",
|
||||||
|
"spoofDialogReloginTitle": "Done!",
|
||||||
|
"spoofDialogReloginContent": "Due to MAX specifics, your spoof is changed, but changes will be visible only after re-login.",
|
||||||
|
"spoofDialogReloginWarning": "Re-login now?",
|
||||||
|
"spoofDialogReloginDeny": "Later",
|
||||||
|
"spoofDialogReloginConfirm": "Re-login now",
|
||||||
"spoofDialogApplyDeny": "No",
|
"spoofDialogApplyDeny": "No",
|
||||||
"spoofDialogApplyConfirm": "Ok!",
|
"spoofDialogApplyConfirm": "Ok!",
|
||||||
"spoofErrorApplyFailed": "Failed to apply settings: {error}",
|
"spoofErrorApplyFailed": "Failed to apply settings: {error}",
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ abstract class AppLocalizations {
|
|||||||
/// No description provided for @spoofDeviceTypeDescription.
|
/// No description provided for @spoofDeviceTypeDescription.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.'**
|
/// **'This field is not changeable, to avoid token association issues'**
|
||||||
String get spoofDeviceTypeDescription;
|
String get spoofDeviceTypeDescription;
|
||||||
|
|
||||||
/// No description provided for @spoofDeviceTypeLabel.
|
/// No description provided for @spoofDeviceTypeLabel.
|
||||||
@@ -572,6 +572,42 @@ abstract class AppLocalizations {
|
|||||||
/// **'Need to reconnect the app, ok?'**
|
/// **'Need to reconnect the app, ok?'**
|
||||||
String get spoofDialogApplyContent;
|
String get spoofDialogApplyContent;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogApplyWarning.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Your spoof will change immediately. But due to MAX specifics, you must re-login to the account for it to become visible'**
|
||||||
|
String get spoofDialogApplyWarning;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogReloginTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Done!'**
|
||||||
|
String get spoofDialogReloginTitle;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogReloginContent.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Due to MAX specifics, your spoof is changed, but changes will be visible only after re-login.'**
|
||||||
|
String get spoofDialogReloginContent;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogReloginWarning.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Re-login now?'**
|
||||||
|
String get spoofDialogReloginWarning;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogReloginDeny.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Later'**
|
||||||
|
String get spoofDialogReloginDeny;
|
||||||
|
|
||||||
|
/// No description provided for @spoofDialogReloginConfirm.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Re-login now'**
|
||||||
|
String get spoofDialogReloginConfirm;
|
||||||
|
|
||||||
/// No description provided for @spoofDialogApplyDeny.
|
/// No description provided for @spoofDialogApplyDeny.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDeviceTypeDescription =>
|
String get spoofDeviceTypeDescription =>
|
||||||
'Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.';
|
'This field is not changeable, to avoid token association issues';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDeviceTypeLabel => 'Device type';
|
String get spoofDeviceTypeLabel => 'Device type';
|
||||||
@@ -256,6 +256,26 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get spoofDialogApplyContent => 'Need to reconnect the app, ok?';
|
String get spoofDialogApplyContent => 'Need to reconnect the app, ok?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogApplyWarning =>
|
||||||
|
'Your spoof will change immediately. But due to MAX specifics, you must re-login to the account for it to become visible';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginTitle => 'Done!';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginContent =>
|
||||||
|
'Due to MAX specifics, your spoof is changed, but changes will be visible only after re-login.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginWarning => 'Re-login now?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginDeny => 'Later';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginConfirm => 'Re-login now';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDialogApplyDeny => 'No';
|
String get spoofDialogApplyDeny => 'No';
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDeviceTypeDescription =>
|
String get spoofDeviceTypeDescription =>
|
||||||
'Выберите тип устройства для генерации пресетов. При нажатии \"Сгенерировать\" будут использоваться только пресеты выбранного типа.';
|
'Данное поле не изменяемое, во избежании проблем с ассоциацией токена';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDeviceTypeLabel => 'Тип устройства';
|
String get spoofDeviceTypeLabel => 'Тип устройства';
|
||||||
@@ -258,6 +258,26 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get spoofDialogApplyContent => 'Нужно перезайти в приложение, ок?';
|
String get spoofDialogApplyContent => 'Нужно перезайти в приложение, ок?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogApplyWarning =>
|
||||||
|
'Ваш спуф изменится сразу. Но из-за особенностей МАХ, для того что-бы это стало заметно, вы должны перелогиниться в аккаунт';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginTitle => 'Готово!';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginContent =>
|
||||||
|
'Из-за особенности МАХ, ваш спуф изменён, но видны изменения будут только при перезаходе в аккаунт.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginWarning => 'Перезайти сейчас?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginDeny => 'Позже';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get spoofDialogReloginConfirm => 'Перелогиниться сейчас';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get spoofDialogApplyDeny => 'Не';
|
String get spoofDialogApplyDeny => 'Не';
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -64,7 +64,7 @@
|
|||||||
"spoofMethodPartialDescription": "Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.",
|
"spoofMethodPartialDescription": "Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.",
|
||||||
"spoofMethodFullDescription": "Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!",
|
"spoofMethodFullDescription": "Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!",
|
||||||
"spoofDeviceTypeTitle": "Тип устройства",
|
"spoofDeviceTypeTitle": "Тип устройства",
|
||||||
"spoofDeviceTypeDescription": "Выберите тип устройства для генерации пресетов. При нажатии \"Сгенерировать\" будут использоваться только пресеты выбранного типа.",
|
"spoofDeviceTypeDescription": "Данное поле не изменяемое, во избежании проблем с ассоциацией токена",
|
||||||
"spoofDeviceTypeLabel": "Тип устройства",
|
"spoofDeviceTypeLabel": "Тип устройства",
|
||||||
"spoofMainSectionTitle": "Основные данные",
|
"spoofMainSectionTitle": "Основные данные",
|
||||||
"spoofFieldDeviceName": "Имя устройства",
|
"spoofFieldDeviceName": "Имя устройства",
|
||||||
@@ -88,6 +88,12 @@
|
|||||||
"spoofDialogYes": "Да",
|
"spoofDialogYes": "Да",
|
||||||
"spoofDialogApplyTitle": "Применить настройки?",
|
"spoofDialogApplyTitle": "Применить настройки?",
|
||||||
"spoofDialogApplyContent": "Нужно перезайти в приложение, ок?",
|
"spoofDialogApplyContent": "Нужно перезайти в приложение, ок?",
|
||||||
|
"spoofDialogApplyWarning": "Ваш спуф изменится сразу. Но из-за особенностей МАХ, для того что-бы это стало заметно, вы должны перелогиниться в аккаунт",
|
||||||
|
"spoofDialogReloginTitle": "Готово!",
|
||||||
|
"spoofDialogReloginContent": "Из-за особенности МАХ, ваш спуф изменён, но видны изменения будут только при перезаходе в аккаунт.",
|
||||||
|
"spoofDialogReloginWarning": "Перезайти сейчас?",
|
||||||
|
"spoofDialogReloginDeny": "Позже",
|
||||||
|
"spoofDialogReloginConfirm": "Перелогиниться сейчас",
|
||||||
"spoofDialogApplyDeny": "Не",
|
"spoofDialogApplyDeny": "Не",
|
||||||
"spoofDialogApplyConfirm": "Ок!",
|
"spoofDialogApplyConfirm": "Ок!",
|
||||||
"spoofErrorApplyFailed": "Ошибка при применении настроек: {error}",
|
"spoofErrorApplyFailed": "Ошибка при применении настроек: {error}",
|
||||||
|
|||||||
+11
-5
@@ -36,6 +36,7 @@ void main() async {
|
|||||||
await AppDatabase.init();
|
await AppDatabase.init();
|
||||||
await api.connect();
|
await api.connect();
|
||||||
final initialLocale = await _loadInitialLocale();
|
final initialLocale = await _loadInitialLocale();
|
||||||
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final initialFpsOverlay = prefs.getBool('dev_fps_overlay') ?? false;
|
final initialFpsOverlay = prefs.getBool('dev_fps_overlay') ?? false;
|
||||||
runApp(
|
runApp(
|
||||||
@@ -70,8 +71,9 @@ class KometAppState extends State<KometApp> {
|
|||||||
|
|
||||||
late Locale _locale;
|
late Locale _locale;
|
||||||
bool _isLoggingOut = false;
|
bool _isLoggingOut = false;
|
||||||
late final ValueNotifier<bool> fpsOverlayEnabled =
|
late final ValueNotifier<bool> fpsOverlayEnabled = ValueNotifier(
|
||||||
ValueNotifier(widget.initialFpsOverlay);
|
widget.initialFpsOverlay,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -79,10 +81,14 @@ class KometAppState extends State<KometApp> {
|
|||||||
_locale = widget.initialLocale;
|
_locale = widget.initialLocale;
|
||||||
|
|
||||||
api.setReconnectCallback(() async {
|
api.setReconnectCallback(() async {
|
||||||
|
try {
|
||||||
final accountId = await TokenStorage.getActiveAccountId();
|
final accountId = await TokenStorage.getActiveAccountId();
|
||||||
if (accountId != null) {
|
if (accountId != null &&
|
||||||
await accountModule.login(accountId: accountId);
|
await TokenStorage.readToken(accountId) != null) {
|
||||||
|
final token = await TokenStorage.readToken(accountId);
|
||||||
|
await accountModule.login(accountId: accountId, token: token);
|
||||||
}
|
}
|
||||||
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
api.sessionExpiredStream.listen((SessionExpiredException e) async {
|
api.sessionExpiredStream.listen((SessionExpiredException e) async {
|
||||||
@@ -250,7 +256,7 @@ class _StartupScreenState extends State<_StartupScreen> {
|
|||||||
|
|
||||||
Future<void> _tryAutoLogin() async {
|
Future<void> _tryAutoLogin() async {
|
||||||
final accountId = await TokenStorage.getActiveAccountId();
|
final accountId = await TokenStorage.getActiveAccountId();
|
||||||
if (accountId == null) {
|
if (accountId == null || await TokenStorage.readToken(accountId) == null) {
|
||||||
_goToLogin();
|
_goToLogin();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user