починил 2фа

This commit is contained in:
Jganenok
2026-05-28 22:05:37 +07:00
parent e4ba80e3be
commit 685c7a7939
2 changed files with 104 additions and 100 deletions
+50 -34
View File
@@ -621,15 +621,17 @@ class AccountModule {
String? hint, String? hint,
}) async { }) async {
_ensureOnline(); _ensureOnline();
final capabilities = <int>[0, if (hint != null) 3, 4];
final payload = <dynamic, dynamic>{ final payload = <dynamic, dynamic>{
'expectedCapabilities': [0, 3, 4], 'expectedCapabilities': capabilities,
'trackId': trackId, 'trackId': trackId,
'password': password, 'password': password,
}; };
if (hint != null) payload['hint'] = hint; if (hint != null) payload['hint'] = hint;
final packet = await _api.sendRequest(Opcode.authSet2fa, payload); return _processProfileUpdate(
_checkPacketError(packet, 'confirm2fa'); _api.sendRequest(Opcode.authSet2fa, payload),
return _processProfileUpdate(packet); 'confirm2fa',
);
} }
// 2FA Management (when already set) // 2FA Management (when already set)
@@ -707,15 +709,16 @@ class AccountModule {
} }
final payload = <dynamic, dynamic>{ final payload = <dynamic, dynamic>{
'expectedCapabilities': [1, 3], 'expectedCapabilities': <int>[1, if (hint != null) 3],
'trackId': trackId, 'trackId': trackId,
'password': newPassword, 'password': newPassword,
}; };
if (hint != null) payload['hint'] = hint; if (hint != null) payload['hint'] = hint;
final packet = await _api.sendRequest(Opcode.authSet2fa, payload); return _processProfileUpdate(
_checkPacketError(packet, 'update2faPassword'); _api.sendRequest(Opcode.authSet2fa, payload),
return _processProfileUpdate(packet); 'update2faPassword',
);
} }
Future<ProfileData> update2faEmail({ Future<ProfileData> update2faEmail({
@@ -740,9 +743,10 @@ class AccountModule {
'expectedCapabilities': [4], 'expectedCapabilities': [4],
'trackId': trackId, 'trackId': trackId,
}; };
final packet = await _api.sendRequest(Opcode.authSet2fa, payload); return _processProfileUpdate(
_checkPacketError(packet, 'update2faEmail'); _api.sendRequest(Opcode.authSet2fa, payload),
return _processProfileUpdate(packet); 'update2faEmail',
);
} }
Future<ProfileData> remove2fa(String trackId) async { Future<ProfileData> remove2fa(String trackId) async {
@@ -752,34 +756,46 @@ class AccountModule {
'trackId': trackId, 'trackId': trackId,
'remove2fa': true, 'remove2fa': true,
}; };
final packet = await _api.sendRequest(Opcode.authSet2fa, payload); return _processProfileUpdate(
_checkPacketError(packet, 'remove2fa'); _api.sendRequest(Opcode.authSet2fa, payload),
return _processProfileUpdate(packet); 'remove2fa',
);
} }
Future<ProfileData> _processProfileUpdate(Packet packet) async { Future<ProfileData> _processProfileUpdate(
_api.registerPushHandler(Opcode.notifProfile, (p) {}); Future<Packet> requestFuture,
try { String tag,
await for (final push in _api.pushStream ) async {
.where((p) => p.opcode == Opcode.notifProfile) final completer = Completer<ProfileData>();
.timeout(const Duration(seconds: 15))) { final sub = _api.pushStream
final payload = push.payload; .where((p) => p.opcode == Opcode.notifProfile)
if (payload is Map) { .listen((push) {
final profile = payload['profile']; if (completer.isCompleted) return;
if (profile is Map) { final payload = push.payload;
final contact = profile['contact']; if (payload is! Map) return;
if (contact is Map) { final profile = payload['profile'];
return ProfileData.fromServerMap(contact.cast<dynamic, dynamic>()); if (profile is! Map) return;
} final contact = profile['contact'];
} if (contact is! Map) return;
} completer.complete(
ProfileData.fromServerMap(contact.cast<dynamic, dynamic>()),
);
});
final timer = Timer(const Duration(seconds: 15), () {
if (!completer.isCompleted) {
completer.completeError(
Exception('Таймаут ожидания обновления профиля'),
);
} }
} on TimeoutException { });
throw Exception('Таймаут ожидания обновления профиля'); try {
final packet = await requestFuture;
_checkPacketError(packet, tag);
return await completer.future;
} finally { } finally {
_api.unregisterPushHandler(Opcode.notifProfile); timer.cancel();
await sub.cancel();
} }
throw Exception('Не удалось получить обновлённый профиль');
} }
Future<RequestCodeResult> requestCode( Future<RequestCodeResult> requestCode(
@@ -564,18 +564,9 @@ class _TwoFactorSetupScreenState extends State<TwoFactorSetupScreen> {
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 14), style: TextStyle(color: cs.onSurfaceVariant, fontSize: 14),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Введите пароль',
decoration: InputDecoration(
hintText: 'Введите пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
], ],
); );
@@ -599,18 +590,9 @@ class _TwoFactorSetupScreenState extends State<TwoFactorSetupScreen> {
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 14), style: TextStyle(color: cs.onSurfaceVariant, fontSize: 14),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Повторите пароль',
decoration: InputDecoration(
hintText: 'Повторите пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
], ],
); );
@@ -818,18 +800,9 @@ class _TwoFactorManageScreenState extends State<TwoFactorManageScreen> {
style: TextStyle(color: cs.onErrorContainer), style: TextStyle(color: cs.onErrorContainer),
), ),
), ),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Пароль',
decoration: InputDecoration(
hintText: 'Пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
SizedBox( SizedBox(
@@ -1035,18 +1008,9 @@ class _TwoFactorPasswordChangeScreenState
style: TextStyle(color: cs.onErrorContainer), style: TextStyle(color: cs.onErrorContainer),
), ),
), ),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Введите новый пароль',
decoration: InputDecoration(
hintText: 'Введите новый пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
@@ -1216,18 +1180,9 @@ class _TwoFactorEmailChangeScreenState
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Пароль',
decoration: InputDecoration(
hintText: 'Пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
] else ...[ ] else ...[
if (_errorMessage != null) if (_errorMessage != null)
@@ -1440,18 +1395,9 @@ class _TwoFactorRemoveScreenState extends State<TwoFactorRemoveScreen> {
style: TextStyle(color: cs.onErrorContainer), style: TextStyle(color: cs.onErrorContainer),
), ),
), ),
TextField( _PasswordField(
controller: _passwordController, controller: _passwordController,
obscureText: true, hintText: 'Пароль',
decoration: InputDecoration(
hintText: 'Пароль',
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
SizedBox( SizedBox(
@@ -1484,3 +1430,45 @@ class _TwoFactorRemoveScreenState extends State<TwoFactorRemoveScreen> {
); );
} }
} }
class _PasswordField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
const _PasswordField({
required this.controller,
required this.hintText,
});
@override
State<_PasswordField> createState() => _PasswordFieldState();
}
class _PasswordFieldState extends State<_PasswordField> {
bool _visible = false;
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return TextField(
controller: widget.controller,
obscureText: !_visible,
decoration: InputDecoration(
hintText: widget.hintText,
filled: true,
fillColor: cs.surfaceContainerHighest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
suffixIcon: IconButton(
icon: Icon(
_visible ? Symbols.visibility_off : Symbols.visibility,
color: cs.onSurfaceVariant,
),
onPressed: () => setState(() => _visible = !_visible),
),
),
);
}
}