Merge branch 'feature/FullStack' of https://github.com/KometTeam/Komet into feature/FullStack

This commit is contained in:
klockky
2026-05-14 13:16:50 +03:00
23 changed files with 1087 additions and 202 deletions
+78 -1
View File
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart' show Locale;
import '../api.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
@@ -431,6 +432,82 @@ class AccountModule {
return config;
}
Future<ProfileData> updateProfileName(String firstName, String? lastName) async {
_ensureOnline();
final payload = <dynamic, dynamic>{
'firstName': firstName,
};
if (lastName != null) payload['lastName'] = lastName;
final packet = await _api.sendRequest(Opcode.profile, payload);
if (packet.isError) {
throw Exception(packet.payload?.toString() ?? 'Server error');
}
final data = packet.payload as Map?;
if (data == null) throw Exception('Empty response');
final profile = data['profile'] as Map?;
if (profile == null) throw Exception('No profile in response');
final contact = profile['contact'] as Map?;
if (contact == null) throw Exception('No contact in response');
final newProfile = ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
await AppDatabase.saveProfile(newProfile, isActive: true);
return newProfile;
}
Future<ProfileData> updateProfileAvatar(String photoToken, String avatarType) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.profile, {
'photoToken': photoToken,
'avatarType': avatarType,
});
if (packet.isError) {
throw Exception(packet.payload?.toString() ?? 'Server error');
}
final data = packet.payload as Map?;
if (data == null) throw Exception('Empty response');
final profile = data['profile'] as Map?;
if (profile == null) throw Exception('No profile in response');
final contact = profile['contact'] as Map?;
if (contact == null) throw Exception('No contact in response');
final newProfile = ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
await AppDatabase.saveProfile(newProfile, isActive: true);
return newProfile;
}
Future<String> getAvatarUploadUrl() async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.photoUpload, {
'count': 1,
'profile': true,
});
if (packet.isError) {
throw Exception(packet.payload?.toString() ?? 'Server error');
}
final data = packet.payload as Map?;
if (data == null) throw Exception('Empty response');
final url = data['url'] as String?;
if (url == null) throw Exception('No url in response');
return url;
}
Future<ProfileData> removeProfilePhoto(int photoId) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.removeContactPhoto, {
'photoId': photoId,
});
if (packet.isError) {
throw Exception(packet.payload?.toString() ?? 'Server error');
}
final data = packet.payload as Map?;
if (data == null) throw Exception('Empty response');
final profile = data['profile'] as Map?;
if (profile == null) throw Exception('No profile in response');
final contact = profile['contact'] as Map?;
if (contact == null) throw Exception('No contact in response');
final newProfile = ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
await AppDatabase.saveProfile(newProfile, isActive: true);
return newProfile;
}
// 2FA Creation (when not set)
Future<String> create2faTrack() async {
_ensureOnline();
@@ -931,7 +1008,7 @@ class AccountModule {
throw Exception('login: отсутствует profile.contact в ответе');
}
final profile = ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
await AppDatabase.saveProfile(profile);
await AppDatabase.saveProfile(profile, isActive: true);
await AppDatabase.setActiveAccount(profile.id);
await _saveSyncState(data, serverTime, profile.id);
+57
View File
@@ -23,6 +23,34 @@ class ContactCache {
static String? getAvatar(int id) => _avatarCache[id];
}
class TranscriptionResult {
final int status;
final String? text;
final String? messageId;
final int? chatId;
final int? mediaId;
TranscriptionResult({
required this.status,
this.text,
this.messageId,
this.chatId,
this.mediaId,
});
}
class TranscriptionCache {
static final Map<String, TranscriptionResult> _cache = {};
static void put(String messageId, TranscriptionResult result) {
_cache[messageId] = result;
}
static TranscriptionResult? get(String messageId) => _cache[messageId];
static bool has(String messageId) => _cache.containsKey(messageId);
}
class CachedMessage {
final String id;
final int accountId;
@@ -247,6 +275,35 @@ class MessagesModule {
await _api.sendRequest(Opcode.msgSend, payload);
}
Future<TranscriptionResult> requestTranscription(
int chatId,
int messageId,
int mediaId,
) async {
final payload = {
'chatId': chatId,
'messageId': messageId,
'mediaId': mediaId,
};
final response = await _api.sendRequest(Opcode.audioTranscription, payload);
if (!response.isOk) return TranscriptionResult(status: -1);
final data = response.payload;
if (data is! Map) return TranscriptionResult(status: -1);
final transcriptionStatus = data['transcriptionStatus'] as int? ?? -1;
if (transcriptionStatus == 1) {
final text = data['transcription'] as String? ?? '';
if (text.isEmpty) {
return TranscriptionResult(status: 1, text: 'не удалось распознать текст');
}
return TranscriptionResult(status: 1, text: text);
}
return TranscriptionResult(status: transcriptionStatus);
}
Future<Uint8List?> downloadPhoto(String baseUrl, String photoToken) async {
try {
final response = await _api.sendRequest(Opcode.fileDownload, {