бля а как мне тестить у меня темпблок
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import '../api.dart';
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
import '../../core/protocol/packet.dart';
|
||||
@@ -9,6 +10,184 @@ import 'chats.dart';
|
||||
import 'contacts.dart';
|
||||
import 'folders.dart';
|
||||
|
||||
class PrivacyConfig {
|
||||
final String searchByPhone;
|
||||
final String incomingCall;
|
||||
final bool doubleTapReactionDisabled;
|
||||
final bool safeModeNoPin;
|
||||
final String? doubleTapReactionValue;
|
||||
final String familyProtection;
|
||||
final bool pushDetails;
|
||||
final bool hidden;
|
||||
final String chatsInvite;
|
||||
final bool pushNewContacts;
|
||||
final bool unsafeFiles;
|
||||
final String inactiveTtl;
|
||||
final bool showReadMark;
|
||||
final bool altKeyboard;
|
||||
final bool contentLevelAccess;
|
||||
final String stickersSuggest;
|
||||
final bool safeMode;
|
||||
final bool audioTranscriptionEnabled;
|
||||
final String hash;
|
||||
|
||||
const PrivacyConfig({
|
||||
required this.searchByPhone,
|
||||
required this.incomingCall,
|
||||
required this.doubleTapReactionDisabled,
|
||||
required this.safeModeNoPin,
|
||||
this.doubleTapReactionValue,
|
||||
required this.familyProtection,
|
||||
required this.pushDetails,
|
||||
required this.hidden,
|
||||
required this.chatsInvite,
|
||||
required this.pushNewContacts,
|
||||
required this.unsafeFiles,
|
||||
required this.inactiveTtl,
|
||||
required this.showReadMark,
|
||||
required this.altKeyboard,
|
||||
required this.contentLevelAccess,
|
||||
required this.stickersSuggest,
|
||||
required this.safeMode,
|
||||
required this.audioTranscriptionEnabled,
|
||||
required this.hash,
|
||||
});
|
||||
|
||||
factory PrivacyConfig.fromMap(Map<dynamic, dynamic> map) {
|
||||
return PrivacyConfig(
|
||||
searchByPhone: map['SEARCH_BY_PHONE']?.toString() ?? 'ALL',
|
||||
incomingCall: map['INCOMING_CALL']?.toString() ?? 'CONTACTS',
|
||||
doubleTapReactionDisabled: map['DOUBLE_TAP_REACTION_DISABLED'] ?? false,
|
||||
safeModeNoPin: map['SAFE_MODE_NO_PIN'] ?? false,
|
||||
doubleTapReactionValue: map['DOUBLE_TAP_REACTION_VALUE']?.toString(),
|
||||
familyProtection: map['FAMILY_PROTECTION']?.toString() ?? 'OFF',
|
||||
pushDetails: map['PUSH_DETAILS'] ?? false,
|
||||
hidden: map['HIDDEN'] ?? true,
|
||||
chatsInvite: map['CHATS_INVITE']?.toString() ?? 'CONTACTS',
|
||||
pushNewContacts: map['PUSH_NEW_CONTACTS'] ?? false,
|
||||
unsafeFiles: map['UNSAFE_FILES'] ?? true,
|
||||
inactiveTtl: map['INACTIVE_TTL']?.toString() ?? '6M',
|
||||
showReadMark: map['SHOW_READ_MARK'] ?? true,
|
||||
altKeyboard: map['ALT_KEYBOARD'] ?? false,
|
||||
contentLevelAccess: map['CONTENT_LEVEL_ACCESS'] ?? false,
|
||||
stickersSuggest: map['STICKERS_SUGGEST']?.toString() ?? 'ON',
|
||||
safeMode: map['SAFE_MODE'] ?? false,
|
||||
audioTranscriptionEnabled: map['AUDIO_TRANSCRIPTION_ENABLED'] ?? true,
|
||||
hash: map['hash']?.toString() ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => jsonEncode({
|
||||
'SEARCH_BY_PHONE': searchByPhone,
|
||||
'INCOMING_CALL': incomingCall,
|
||||
'DOUBLE_TAP_REACTION_DISABLED': doubleTapReactionDisabled,
|
||||
'SAFE_MODE_NO_PIN': safeModeNoPin,
|
||||
'DOUBLE_TAP_REACTION_VALUE': doubleTapReactionValue,
|
||||
'FAMILY_PROTECTION': familyProtection,
|
||||
'PUSH_DETAILS': pushDetails,
|
||||
'HIDDEN': hidden,
|
||||
'CHATS_INVITE': chatsInvite,
|
||||
'PUSH_NEW_CONTACTS': pushNewContacts,
|
||||
'UNSAFE_FILES': unsafeFiles,
|
||||
'INACTIVE_TTL': inactiveTtl,
|
||||
'SHOW_READ_MARK': showReadMark,
|
||||
'ALT_KEYBOARD': altKeyboard,
|
||||
'CONTENT_LEVEL_ACCESS': contentLevelAccess,
|
||||
'STICKERS_SUGGEST': stickersSuggest,
|
||||
'SAFE_MODE': safeMode,
|
||||
'AUDIO_TRANSCRIPTION_ENABLED': audioTranscriptionEnabled,
|
||||
'hash': hash,
|
||||
});
|
||||
|
||||
factory PrivacyConfig.fromJson(String json) {
|
||||
try {
|
||||
final map = jsonDecode(json) as Map<String, dynamic>;
|
||||
return PrivacyConfig.fromMap(map);
|
||||
} catch (_) {
|
||||
return PrivacyConfig.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static PrivacyConfig empty() {
|
||||
return const PrivacyConfig(
|
||||
searchByPhone: 'ALL',
|
||||
incomingCall: 'CONTACTS',
|
||||
doubleTapReactionDisabled: false,
|
||||
safeModeNoPin: false,
|
||||
familyProtection: 'OFF',
|
||||
pushDetails: false,
|
||||
hidden: true,
|
||||
chatsInvite: 'CONTACTS',
|
||||
pushNewContacts: false,
|
||||
unsafeFiles: true,
|
||||
inactiveTtl: '6M',
|
||||
showReadMark: true,
|
||||
altKeyboard: false,
|
||||
contentLevelAccess: false,
|
||||
stickersSuggest: 'ON',
|
||||
safeMode: false,
|
||||
audioTranscriptionEnabled: true,
|
||||
hash: '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BlockedContact {
|
||||
final int id;
|
||||
final String? firstName;
|
||||
final String? lastName;
|
||||
final String? baseUrl;
|
||||
final int? photoId;
|
||||
final String status;
|
||||
final int registrationTime;
|
||||
final int updateTime;
|
||||
|
||||
const BlockedContact({
|
||||
required this.id,
|
||||
this.firstName,
|
||||
this.lastName,
|
||||
this.baseUrl,
|
||||
this.photoId,
|
||||
required this.status,
|
||||
required this.registrationTime,
|
||||
required this.updateTime,
|
||||
});
|
||||
|
||||
factory BlockedContact.fromMap(Map<dynamic, dynamic> map) {
|
||||
String? firstName;
|
||||
String? lastName;
|
||||
final names = map['names'] as List?;
|
||||
if (names != null && names.isNotEmpty) {
|
||||
for (final n in names) {
|
||||
if (n is Map) {
|
||||
firstName = n['firstName'] as String?;
|
||||
lastName = n['lastName'] as String?;
|
||||
if (n['type'] == 'ONEME') break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BlockedContact(
|
||||
id: map['id'] as int? ?? 0,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
baseUrl: map['baseUrl'] as String?,
|
||||
photoId: map['photoId'] as int?,
|
||||
status: map['status']?.toString() ?? 'BLOCKED',
|
||||
registrationTime: map['registrationTime'] as int? ?? 0,
|
||||
updateTime: map['updateTime'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TwoFactorDetails {
|
||||
final bool enabled;
|
||||
final String? email;
|
||||
final String? hint;
|
||||
|
||||
const TwoFactorDetails({required this.enabled, this.email, this.hint});
|
||||
}
|
||||
|
||||
enum AuthRequestType {
|
||||
startAuth('START_AUTH'),
|
||||
resend('RESEND'),
|
||||
@@ -182,6 +361,303 @@ class AccountModule {
|
||||
|
||||
Stream<LoginStatus> get loginStatusStream => _loginStatusController.stream;
|
||||
|
||||
Future<PrivacyConfig> getPrivacyConfig() async {
|
||||
final accountId = await TokenStorage.getActiveAccountId();
|
||||
if (accountId != null) {
|
||||
final saved = await AppDatabase.getPrivacyConfig(accountId);
|
||||
if (saved != null) {
|
||||
return PrivacyConfig.fromJson(saved);
|
||||
}
|
||||
}
|
||||
return PrivacyConfig.empty();
|
||||
}
|
||||
|
||||
Future<List<BlockedContact>> getBlockedContacts() async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.contactList, {
|
||||
'status': 'BLOCKED',
|
||||
'count': 100,
|
||||
'from': 0,
|
||||
});
|
||||
_checkPacketError(packet, 'getBlockedContacts');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'getBlockedContacts: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final contacts = data['contacts'] as List?;
|
||||
if (contacts == null) return [];
|
||||
return contacts
|
||||
.whereType<Map>()
|
||||
.map((c) => BlockedContact.fromMap(c.cast<dynamic, dynamic>()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<PrivacyConfig> updatePrivacyConfig(
|
||||
Map<String, dynamic> settings,
|
||||
) async {
|
||||
_ensureOnline();
|
||||
final payload = <dynamic, dynamic>{
|
||||
'settings': {'user': settings},
|
||||
};
|
||||
final packet = await _api.sendRequest(Opcode.config, payload);
|
||||
_checkPacketError(packet, 'updatePrivacyConfig');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'updatePrivacyConfig: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final user = data['user'];
|
||||
if (user is! Map) {
|
||||
throw Exception('updatePrivacyConfig: отсутствует user в payload');
|
||||
}
|
||||
final config = PrivacyConfig.fromMap(user.cast<dynamic, dynamic>());
|
||||
final accountId = await TokenStorage.getActiveAccountId();
|
||||
if (accountId != null) {
|
||||
await AppDatabase.savePrivacyConfig(accountId, config.toJson());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// 2FA Creation (when not set)
|
||||
Future<String> create2faTrack() async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authCreateTrack, {'type': 0});
|
||||
_checkPacketError(packet, 'create2faTrack');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'create2faTrack: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final trackId = data['trackId'] as String?;
|
||||
if (trackId == null) {
|
||||
throw Exception('create2faTrack: отсутствует trackId');
|
||||
}
|
||||
return trackId;
|
||||
}
|
||||
|
||||
Future<void> set2faPassword(String trackId, String password) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authValidatePassword, {
|
||||
'trackId': trackId,
|
||||
'password': password,
|
||||
});
|
||||
_checkPacketError(packet, 'set2faPassword');
|
||||
if (packet.payload != null && packet.payload is! Map) {
|
||||
throw Exception('set2faPassword: неожиданный ответ');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> set2faHint(String trackId, String hint) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authValidateHint, {
|
||||
'trackId': trackId,
|
||||
'hint': hint,
|
||||
});
|
||||
_checkPacketError(packet, 'set2faHint');
|
||||
if (packet.payload != null && packet.payload is! Map) {
|
||||
throw Exception('set2faHint: неожиданный ответ');
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> verify2faEmail(String trackId, String email) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authVerifyEmail, {
|
||||
'trackId': trackId,
|
||||
'email': email,
|
||||
});
|
||||
_checkPacketError(packet, 'verify2faEmail');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'verify2faEmail: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final blockingDuration = data['blockingDuration'] as int? ?? 60;
|
||||
return blockingDuration;
|
||||
}
|
||||
|
||||
Future<String> verify2faCode(String trackId, String code) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authCheckEmail, {
|
||||
'trackId': trackId,
|
||||
'verifyCode': code,
|
||||
});
|
||||
_checkPacketError(packet, 'verify2faCode');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'verify2faCode: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final email = data['email'] as String? ?? '';
|
||||
return email;
|
||||
}
|
||||
|
||||
Future<ProfileData> confirm2fa({
|
||||
required String trackId,
|
||||
required String password,
|
||||
String? hint,
|
||||
}) async {
|
||||
_ensureOnline();
|
||||
final payload = <dynamic, dynamic>{
|
||||
'expectedCapabilities': [0, 3, 4],
|
||||
'trackId': trackId,
|
||||
'password': password,
|
||||
};
|
||||
if (hint != null) payload['hint'] = hint;
|
||||
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
|
||||
_checkPacketError(packet, 'confirm2fa');
|
||||
return _processProfileUpdate(packet);
|
||||
}
|
||||
|
||||
// 2FA Management (when already set)
|
||||
Future<String> enter2faPanel() async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authCreateTrack, {'type': 0});
|
||||
_checkPacketError(packet, 'enter2faPanel');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'enter2faPanel: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final trackId = data['trackId'] as String?;
|
||||
if (trackId == null) {
|
||||
throw Exception('enter2faPanel: отсутствует trackId');
|
||||
}
|
||||
return trackId;
|
||||
}
|
||||
|
||||
Future<TwoFactorDetails> get2faDetails(String trackId) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.auth2faDetails, {
|
||||
'trackId': trackId,
|
||||
});
|
||||
_checkPacketError(packet, 'get2faDetails');
|
||||
final data = packet.payload;
|
||||
if (data is! Map) {
|
||||
throw Exception(
|
||||
'get2faDetails: неожиданный тип payload: ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
final password = data['password'] as Map?;
|
||||
return TwoFactorDetails(
|
||||
enabled: password?['enabled'] ?? false,
|
||||
email: password?['email'] as String?,
|
||||
hint: password?['hint'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> check2faPassword(String trackId, String password) async {
|
||||
_ensureOnline();
|
||||
final packet = await _api.sendRequest(Opcode.authLoginCheckPassword, {
|
||||
'trackId': trackId,
|
||||
'password': password,
|
||||
});
|
||||
_checkPacketError(packet, 'check2faPassword');
|
||||
final data = packet.payload;
|
||||
if (data is Map && data['error'] != null) {
|
||||
throw Exception('Неверный пароль');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ProfileData> update2faPassword({
|
||||
required String trackId,
|
||||
required String newPassword,
|
||||
String? hint,
|
||||
}) async {
|
||||
_ensureOnline();
|
||||
final validatePacket = await _api.sendRequest(Opcode.authValidatePassword, {
|
||||
'trackId': trackId,
|
||||
'password': newPassword,
|
||||
});
|
||||
_checkPacketError(validatePacket, 'update2faPassword: validate');
|
||||
if (validatePacket.payload != null && validatePacket.payload is! Map) {
|
||||
throw Exception('update2faPassword: неожиданный ответ при валидации');
|
||||
}
|
||||
|
||||
if (hint != null) {
|
||||
final hintPacket = await _api.sendRequest(Opcode.authValidateHint, {
|
||||
'trackId': trackId,
|
||||
'hint': hint,
|
||||
});
|
||||
_checkPacketError(hintPacket, 'update2faPassword: hint');
|
||||
}
|
||||
|
||||
final payload = <dynamic, dynamic>{
|
||||
'expectedCapabilities': [1, 3],
|
||||
'trackId': trackId,
|
||||
'password': newPassword,
|
||||
};
|
||||
if (hint != null) payload['hint'] = hint;
|
||||
|
||||
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
|
||||
_checkPacketError(packet, 'update2faPassword');
|
||||
return _processProfileUpdate(packet);
|
||||
}
|
||||
|
||||
Future<ProfileData> update2faEmail({
|
||||
required String trackId,
|
||||
required String email,
|
||||
required String code,
|
||||
}) async {
|
||||
_ensureOnline();
|
||||
final verifyPacket = await _api.sendRequest(Opcode.authVerifyEmail, {
|
||||
'trackId': trackId,
|
||||
'email': email,
|
||||
});
|
||||
_checkPacketError(verifyPacket, 'update2faEmail: verify');
|
||||
|
||||
final codePacket = await _api.sendRequest(Opcode.authCheckEmail, {
|
||||
'trackId': trackId,
|
||||
'verifyCode': code,
|
||||
});
|
||||
_checkPacketError(codePacket, 'update2faEmail: code');
|
||||
|
||||
final payload = <dynamic, dynamic>{
|
||||
'expectedCapabilities': [4],
|
||||
'trackId': trackId,
|
||||
};
|
||||
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
|
||||
_checkPacketError(packet, 'update2faEmail');
|
||||
return _processProfileUpdate(packet);
|
||||
}
|
||||
|
||||
Future<ProfileData> remove2fa(String trackId) async {
|
||||
_ensureOnline();
|
||||
final payload = <dynamic, dynamic>{
|
||||
'expectedCapabilities': [5],
|
||||
'trackId': trackId,
|
||||
'remove2fa': true,
|
||||
};
|
||||
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
|
||||
_checkPacketError(packet, 'remove2fa');
|
||||
return _processProfileUpdate(packet);
|
||||
}
|
||||
|
||||
Future<ProfileData> _processProfileUpdate(Packet packet) async {
|
||||
_api.registerPushHandler(Opcode.notifProfile, (p) {});
|
||||
await for (final push in _api.pushStream.where(
|
||||
(p) => p.opcode == Opcode.notifProfile,
|
||||
)) {
|
||||
final payload = push.payload;
|
||||
if (payload is Map) {
|
||||
final profile = payload['profile'];
|
||||
if (profile is Map) {
|
||||
final contact = profile['contact'];
|
||||
if (contact is Map) {
|
||||
return ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw Exception('Не удалось получить обновлённый профиль');
|
||||
}
|
||||
|
||||
Future<RequestCodeResult> requestCode(
|
||||
String phone, {
|
||||
String language = 'ru',
|
||||
|
||||
@@ -14,6 +14,7 @@ class ProfileData {
|
||||
final String country;
|
||||
final int accountStatus;
|
||||
final int updateTime;
|
||||
final List<int>? profileOptions;
|
||||
|
||||
ProfileData({
|
||||
required this.id,
|
||||
@@ -26,6 +27,7 @@ class ProfileData {
|
||||
required this.country,
|
||||
required this.accountStatus,
|
||||
required this.updateTime,
|
||||
this.profileOptions,
|
||||
});
|
||||
|
||||
factory ProfileData.fromServerMap(Map<dynamic, dynamic> contact) {
|
||||
@@ -44,6 +46,12 @@ class ProfileData {
|
||||
lastName = name['lastName'] as String?;
|
||||
}
|
||||
|
||||
final profileOptionsRaw = contact['profileOptions'];
|
||||
List<int>? profileOptions;
|
||||
if (profileOptionsRaw is List) {
|
||||
profileOptions = profileOptionsRaw.map((e) => e as int).toList();
|
||||
}
|
||||
|
||||
return ProfileData(
|
||||
id: contact['id'] as int,
|
||||
firstName: firstName,
|
||||
@@ -55,21 +63,31 @@ class ProfileData {
|
||||
country: (contact['country'] as String?) ?? '',
|
||||
accountStatus: (contact['accountStatus'] as int?) ?? 0,
|
||||
updateTime: (contact['updateTime'] as int?) ?? 0,
|
||||
profileOptions: profileOptions,
|
||||
);
|
||||
}
|
||||
|
||||
factory ProfileData.fromDbRow(Map<String, dynamic> row) {
|
||||
final profileOptionsStr = row['profile_options'] as String?;
|
||||
List<int>? profileOptions;
|
||||
if (profileOptionsStr != null && profileOptionsStr.isNotEmpty) {
|
||||
profileOptions = profileOptionsStr
|
||||
.split(',')
|
||||
.map((e) => int.parse(e.trim()))
|
||||
.toList();
|
||||
}
|
||||
return ProfileData(
|
||||
id: row['id'] as int,
|
||||
firstName: row['first_name'] as String,
|
||||
firstName: (row['first_name'] as String?) ?? '',
|
||||
lastName: row['last_name'] as String?,
|
||||
phone: row['phone'] as int,
|
||||
phone: (row['phone'] as int?) ?? 0,
|
||||
photoId: row['photo_id'] as int?,
|
||||
baseUrl: row['base_url'] as String?,
|
||||
baseRawUrl: row['base_raw_url'] as String?,
|
||||
country: row['country'] as String,
|
||||
accountStatus: row['account_status'] as int,
|
||||
updateTime: row['update_time'] as int,
|
||||
country: (row['country'] as String?) ?? '',
|
||||
accountStatus: (row['account_status'] as int?) ?? 0,
|
||||
updateTime: (row['update_time'] as int?) ?? 0,
|
||||
profileOptions: profileOptions,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,6 +102,7 @@ class ProfileData {
|
||||
'country': country,
|
||||
'account_status': accountStatus,
|
||||
'update_time': updateTime,
|
||||
'profile_options': profileOptions?.join(','),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,7 +138,7 @@ class AppDatabase {
|
||||
final dbPath = await getDatabasesPath();
|
||||
return openDatabase(
|
||||
join(dbPath, 'komet.db'),
|
||||
version: 6,
|
||||
version: 7,
|
||||
onOpen: (db) => db.execute('PRAGMA foreign_keys = ON'),
|
||||
onCreate: (db, _) => _createTables(db),
|
||||
onUpgrade: (db, oldVersion, newVersion) async {
|
||||
@@ -143,6 +162,11 @@ class AppDatabase {
|
||||
if (oldVersion < 6) {
|
||||
await db.execute(_messagesSchema);
|
||||
}
|
||||
if (oldVersion < 7) {
|
||||
await db.execute(
|
||||
'ALTER TABLE profile ADD COLUMN profile_options TEXT',
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -160,7 +184,8 @@ class AppDatabase {
|
||||
country TEXT NOT NULL DEFAULT '',
|
||||
account_status INTEGER NOT NULL DEFAULT 0,
|
||||
update_time INTEGER NOT NULL DEFAULT 0,
|
||||
is_active INTEGER NOT NULL DEFAULT 0
|
||||
is_active INTEGER NOT NULL DEFAULT 0,
|
||||
profile_options TEXT
|
||||
)
|
||||
''');
|
||||
await db.execute(_syncStateSchema);
|
||||
@@ -318,6 +343,30 @@ class AppDatabase {
|
||||
};
|
||||
}
|
||||
|
||||
static Future<void> savePrivacyConfig(
|
||||
int accountId,
|
||||
String jsonConfig,
|
||||
) async {
|
||||
final db = await _instance;
|
||||
await db.insert('sync_state', {
|
||||
'account_id': accountId,
|
||||
'key': 'privacy_config',
|
||||
'value': jsonConfig,
|
||||
}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
static Future<String?> getPrivacyConfig(int accountId) async {
|
||||
final db = await _instance;
|
||||
final rows = await db.query(
|
||||
'sync_state',
|
||||
where: 'account_id = ? AND key = ?',
|
||||
whereArgs: [accountId, 'privacy_config'],
|
||||
limit: 1,
|
||||
);
|
||||
if (rows.isEmpty) return null;
|
||||
return rows.first['value'] as String;
|
||||
}
|
||||
|
||||
static Future<void> close() async {
|
||||
await _db?.close();
|
||||
_db = null;
|
||||
|
||||
@@ -2,7 +2,6 @@ import '../protocol/packet.dart';
|
||||
import '../utils/logger.dart';
|
||||
import 'connection.dart';
|
||||
|
||||
/// Упаковывает и отправляет пакеты, ведёт счётчик seq.
|
||||
class PacketSender {
|
||||
int _seq = 0;
|
||||
|
||||
@@ -13,7 +12,6 @@ class PacketSender {
|
||||
return _seq;
|
||||
}
|
||||
|
||||
/// Отправляет пакет, возвращает присвоенный seq.
|
||||
int send(Connection connection, int opcode, Map<dynamic, dynamic> payload) {
|
||||
final seq = _nextSeq();
|
||||
final data = packPacket(opcode, payload, seq: seq);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -21,10 +21,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -313,18 +313,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
material_symbols_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -638,10 +638,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.10"
|
||||
timezone:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user