Rebrand application as Qlyra
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s

This commit is contained in:
sevenhill
2026-08-30 13:16:17 +03:00
parent 0245f792de
commit f74057ce9b
592 changed files with 38233 additions and 1899 deletions
+12 -12
View File
@@ -4,13 +4,13 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:kolibri/kolibri.dart';
import 'package:kusoft/kusoft.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import '../core/cache/self_presence.dart';
import '../core/config/config.dart';
import '../core/config/countries.dart';
import '../core/config/komet_settings.dart';
import '../core/config/qlyra_settings.dart';
import '../core/config/proxy_config.dart';
import '../core/protocol/opcode_map.dart';
import '../core/protocol/packet.dart';
@@ -27,11 +27,11 @@ enum SessionState { disconnected, connecting, connected, online }
/// Клиент API.
///
/// Тонкий адаптер над Rust-ядром [KolibriSession] (пакет kolibri): подключение,
/// Тонкий адаптер над Rust-ядром [KusoftSession] (пакет kusoft): подключение,
/// хэндшейк, пинг и реконнект живут в ядре, здесь — оркестрация жизненного цикла
/// и сохранение прежнего интерфейса для модулей (Packet/пуши/стримы).
class Api {
KolibriSession? _session;
KusoftSession? _session;
/// Роутер пушей (ответы на запросы ядро матчит само, диспетчер держим только
/// ради registerHandler/pushStream).
@@ -60,7 +60,7 @@ class Api {
String? get callsOsVersion => _callsOsVersion;
/// Сырой доступ к сессии ядра — для медиа-загрузок (data-plane).
KolibriSession? get session => _session;
KusoftSession? get session => _session;
String? spoofScope;
@@ -284,7 +284,7 @@ class Api {
}
// Лог запроса/ответа ведётся из wire-лога ядра (_onWireLog) по настоящему
// проводному seq, поэтому здесь ничего не пишем.
final KolibriResponse resp = await session
final KusoftResponse resp = await session
.requestMapFull(opcode, Map<String, dynamic>.from(payload))
.timeout(
ServerConfig.requestTimeout,
@@ -368,7 +368,7 @@ class Api {
/// Строит устройство-поля и создаёт сессию ядра. Заодно заполняет
/// [_userAgent] и [_deviceId] для геттеров.
Future<(KolibriSession, Stream<WireLogEvent>)> _buildSessionOptions(
Future<(KusoftSession, Stream<WireLogEvent>)> _buildSessionOptions(
({String host, int port, bool trustMincifryCa}) endpoint,
) async {
final deviceInfo = DeviceInfoPlugin();
@@ -513,7 +513,7 @@ class Api {
deviceLocale: deviceLocale,
clientSessionId: clientSessionId,
pingIntervalSecs: ServerConfig.pingInterval.inSeconds,
pingInteractive: !KometSettings.ghostMode.value,
pingInteractive: !QlyraSettings.ghostMode.value,
autoReconnect: false,
insecureTls: insecureTls,
proxy: proxy,
@@ -665,7 +665,7 @@ class Api {
try {
await session
.requestMapFull(Opcode.ping, {
'interactive': !KometSettings.ghostMode.value,
'interactive': !QlyraSettings.ghostMode.value,
})
.timeout(const Duration(seconds: 6));
} catch (_) {
@@ -720,7 +720,7 @@ class Api {
/// синхронизация interactive-флага пинга и присутствия.
void _startLiveness() {
_livenessTimer?.cancel();
_lastInteractive = !KometSettings.ghostMode.value;
_lastInteractive = !QlyraSettings.ghostMode.value;
_livenessTimer = Timer.periodic(_livenessInterval, (_) => _tickLiveness());
}
@@ -729,11 +729,11 @@ class Api {
if (session == null || _sessionState != SessionState.online) return;
final st = session.state();
if (st != 'online' && st != 'connected') {
logger.w('kolibri сессия "$st" — реконнект');
logger.w('kusoft сессия "$st" — реконнект');
_onDisconnected();
return;
}
final interactive = !KometSettings.ghostMode.value;
final interactive = !QlyraSettings.ghostMode.value;
if (interactive != _lastInteractive) {
_lastInteractive = interactive;
try {
+11 -5
View File
@@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:typed_data';
import '../api.dart';
import '../../core/config/debug_test.dart';
import '../../core/config/komet_settings.dart';
import '../../core/config/qlyra_settings.dart';
import '../../core/protocol/chat_cache_fingerprint.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
@@ -534,7 +534,7 @@ class AccountModule {
}) {
final payload = <dynamic, dynamic>{
'token': token,
'interactive': interactive ?? !KometSettings.ghostMode.value,
'interactive': interactive ?? !QlyraSettings.ghostMode.value,
'exp': {
'chatsCountGroups': Uint8List.fromList([0x0b, 0x32]),
},
@@ -645,7 +645,9 @@ class AccountModule {
Future<ProfileData> _resurrectProfile(int accountId) async {
if (DebugTest.berserk) {
await AppDatabase.deleteAccount(accountId);
logger.w('login: [BERSERK] профиль удалён из БД, форсирую регенерацию (id=$accountId)');
logger.w(
'login: [BERSERK] профиль удалён из БД, форсирую регенерацию (id=$accountId)',
);
} else {
final cached = await AppDatabase.loadProfile(accountId);
if (cached != null) return cached;
@@ -656,11 +658,15 @@ class AccountModule {
try {
final fetched = await ContactsModule.fetchSelfProfile(_api, accountId);
if (fetched != null) {
logger.i('login: профиль восстановлен через CONTACT_INFO (id=$accountId)');
logger.i(
'login: профиль восстановлен через CONTACT_INFO (id=$accountId)',
);
return fetched;
}
} catch (e) {
logger.w('login: восстановление профиля через CONTACT_INFO не удалось: $e');
logger.w(
'login: восстановление профиля через CONTACT_INFO не удалось: $e',
);
}
logger.w('login: профиль недоступен, использую заглушку (id=$accountId)');
+1 -1
View File
@@ -20,7 +20,7 @@ class AnimojiModule {
'😍',
];
static const String _recentsKey = 'komet_recent_animoji';
static const String _recentsKey = 'qlyra_recent_animoji';
static const int _maxRecents = 24;
final Map<int, Animoji> _byId = {};
+8 -7
View File
@@ -266,13 +266,14 @@ int? _otherParticipantId(dynamic participants, int currentUserId) {
String? _nameFromContact(Map<dynamic, dynamic> contact) {
final names = contact['names'];
if (names is! List || names.isEmpty) return null;
final nameRaw = names.firstWhere(
(n) => n is Map && n['type'] == 'ONEME',
orElse: () => names.firstWhere((n) => n is Map, orElse: () => null),
);
if (nameRaw is! Map) return null;
final name = nameRaw;
return name['name'] as String?;
Map<dynamic, dynamic>? fallback;
for (final entry in names) {
if (entry is! Map) continue;
final name = entry.cast<dynamic, dynamic>();
fallback ??= name;
if (name['type'] == 'ONEME') return name['name'] as String?;
}
return fallback?['name'] as String?;
}
List<ChatSearchHit> parseSearchResult(dynamic payload) {
+7 -7
View File
@@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import '../../core/config/komet_settings.dart';
import '../../core/config/qlyra_settings.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
import '../../core/cache/info_cache.dart';
@@ -446,7 +446,7 @@ class ChatsModule {
/// Sentinel в `lastMsgText` когда последнее сообщение в чате удалено,
/// а кеша истории нет — UI должен отрисовать курсивную плашку.
static const String lastMsgPlaceholder = '__komet_lastmsg_placeholder__';
static const String lastMsgPlaceholder = '__qlyra_lastmsg_placeholder__';
ChatsModule._();
@@ -472,7 +472,7 @@ class ChatsModule {
if ((row['unread_count'] as int? ?? 0) == 0) return;
final msgIdNum = int.tryParse(messageId);
if (msgIdNum != null && !KometSettings.antiRead.value) {
if (msgIdNum != null && !QlyraSettings.antiRead.value) {
try {
await api.sendRequest(Opcode.chatMark, {
'type': 'READ_MESSAGE',
@@ -501,7 +501,7 @@ class ChatsModule {
required int remaining,
}) async {
final msgIdNum = int.tryParse(messageId);
if (msgIdNum != null && !KometSettings.antiRead.value) {
if (msgIdNum != null && !QlyraSettings.antiRead.value) {
try {
await api.sendRequest(Opcode.chatMark, {
'type': 'READ_MESSAGE',
@@ -737,7 +737,7 @@ class ChatsModule {
}
if (chatId == null) return;
final keepDeleted = KometSettings.viewDeleted.value;
final keepDeleted = QlyraSettings.viewDeleted.value;
final ids = payload['messageIds'];
if (ids is List) {
for (final raw in ids) {
@@ -803,7 +803,7 @@ class ChatsModule {
}
if (status == 'REMOVED' && msgIdStr != null) {
final keepDeleted = KometSettings.viewDeleted.value;
final keepDeleted = QlyraSettings.viewDeleted.value;
if (keepDeleted) {
await AppDatabase.markMessageDeleted(accountId, chatId, msgIdStr);
} else {
@@ -847,7 +847,7 @@ class ChatsModule {
mergedPayload[entry.key.toString()] = entry.value;
}
final newRow = Map<String, dynamic>.from(existing);
if (KometSettings.viewRedacted.value) {
if (QlyraSettings.viewRedacted.value) {
final oldText = existing['text']?.toString();
if ((oldText ?? '') != (msgText ?? '') &&
oldText != null &&
+3 -6
View File
@@ -11,11 +11,7 @@ class CommentsInfo {
final String postId;
final int? totalCount;
final int? updatedAt;
const CommentsInfo({
required this.postId,
this.totalCount,
this.updatedAt,
});
const CommentsInfo({required this.postId, this.totalCount, this.updatedAt});
factory CommentsInfo.fromPayload(String postId, Map payload) {
final raw = payload['totalCount'];
@@ -98,7 +94,8 @@ class CommentsModule {
final link = msg['link'];
final postId =
(payload['postId'] ?? (link is Map ? link['postId'] : null) ??
(payload['postId'] ??
(link is Map ? link['postId'] : null) ??
msg['postId'])
?.toString();
if (postId == null || postId.isEmpty) return;
+1 -1
View File
@@ -577,7 +577,7 @@ class ContactsModule {
firstName: '$first ${i + 1}',
lastName: last,
phone: 79000000000 + i,
baseUrl: 'https://i.pravatar.cc/150?u=komet_debug_$i',
baseUrl: 'https://i.pravatar.cc/150?u=qlyra_debug_$i',
updateTime: 1,
options: i % 6 == 0 ? const {'OFFICIAL'} : const {},
),
+31 -35
View File
@@ -3,7 +3,7 @@ import 'dart:convert' show jsonDecode, utf8;
import 'dart:io';
import 'dart:typed_data';
import 'package:kolibri/kolibri.dart' as kb;
import 'package:kusoft/kusoft.dart' as kb;
import '../api.dart';
import '../../core/protocol/opcode_map.dart';
@@ -47,7 +47,7 @@ class UploadError extends UploadEvent {
}
/// Оркестратор медиа-загрузок: control-plane (URL, отправка сообщения) идёт
/// обычными опкодами, data-plane (заливка на CDN) — через Rust-ядро kolibri,
/// обычными опкодами, data-plane (заливка на CDN) — через Rust-ядро kusoft,
/// которое стримит файл с диска (не держит его целиком в памяти).
class FileUploader {
final Api api;
@@ -101,39 +101,35 @@ class FileUploader {
var status = 0;
String? error;
final done = Completer<void>();
sub =
session
.uploadFilePath(
url: info.url,
path: file.path,
filename: filename,
connection: 'close',
)
.listen(
(e) {
switch (e) {
case kb.UploadEvent_Progress(:final sent, :final total):
ctrl.add(
UploadProgress(
sent: sent.toInt(),
total: total.toInt(),
),
);
case kb.UploadEvent_Done(status: final s):
status = s;
case kb.UploadEvent_Error(:final message):
error = message;
}
},
onError: (Object err) {
error = err.toString();
if (!done.isCompleted) done.complete();
},
onDone: () {
if (!done.isCompleted) done.complete();
},
cancelOnError: true,
);
sub = session
.uploadFilePath(
url: info.url,
path: file.path,
filename: filename,
connection: 'close',
)
.listen(
(e) {
switch (e) {
case kb.UploadEvent_Progress(:final sent, :final total):
ctrl.add(
UploadProgress(sent: sent.toInt(), total: total.toInt()),
);
case kb.UploadEvent_Done(status: final s):
status = s;
case kb.UploadEvent_Error(:final message):
error = message;
}
},
onError: (Object err) {
error = err.toString();
if (!done.isCompleted) done.complete();
},
onDone: () {
if (!done.isCompleted) done.complete();
},
cancelOnError: true,
);
await done.future;
if (cancelled) return;
if (error != null) {
+2 -2
View File
@@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../api.dart';
import '../../core/config/komet_settings.dart';
import '../../core/config/qlyra_settings.dart';
import '../../core/contacts/device_contacts_service.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
@@ -737,7 +737,7 @@ class MessagesModule {
}
}
final toSave = KometSettings.viewRedacted.value && results.isNotEmpty
final toSave = QlyraSettings.viewRedacted.value && results.isNotEmpty
? await _mergeEditHistory(accountId, chatId, results)
: results;
+2 -2
View File
@@ -2,7 +2,7 @@ import 'dart:async';
import '../../core/cache/info_cache.dart';
import '../../core/cache/self_presence.dart';
import '../../core/config/komet_settings.dart';
import '../../core/config/qlyra_settings.dart';
import '../../core/storage/token_storage.dart';
import '../../core/utils/logger.dart';
import '../api.dart';
@@ -40,7 +40,7 @@ class SelfCheckService {
Future<void> _check() async {
final api = _api;
if (api == null || api.state != SessionState.online) return;
if (!KometSettings.selfOnlineCheck.value) return;
if (!QlyraSettings.selfOnlineCheck.value) return;
final accountId = await TokenStorage.getActiveAccountId();
if (accountId == null) return;
final presence = await PresenceFetch.get(accountId, forceRefresh: true);
+4 -7
View File
@@ -45,9 +45,7 @@ class StoriesModule {
int _nowMs() => DateTime.now().millisecondsSinceEpoch;
int _normMs(int t) => t <= 0
? 0
: (t < 1000000000000 ? t * 1000 : t);
int _normMs(int t) => t <= 0 ? 0 : (t < 1000000000000 ? t * 1000 : t);
// ── Кэш (SQLite) ───────────────────────────────────────────────────────
@@ -196,9 +194,7 @@ class StoriesModule {
final chunk = missing.sublist(i, end);
try {
final packet = await _api.sendRequest(Opcode.storiesGetByOwner, {
'owners': [
for (final id in chunk) StoryOwner(ownerId: id).toMap(),
],
'owners': [for (final id in chunk) StoryOwner(ownerId: id).toMap()],
}, silent: true);
if (packet.isError) continue;
if (_applyOwnerPayload(packet.payload, chunk)) changed = true;
@@ -230,7 +226,8 @@ class StoriesModule {
}
}
for (final id in requested) {
if (!seen.contains(id) && _peerPreviews.remove(id) != null) changed = true;
if (!seen.contains(id) && _peerPreviews.remove(id) != null)
changed = true;
}
final rawPeers = data['peerStories'];
if (rawPeers is List) {
@@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import '../../l10n/app_localizations.dart';
import '../../main.dart' show KometApp;
import '../../main.dart' show QlyraApp;
enum UploadKind { photo, video, videoNote, voice, file }
@@ -53,7 +53,7 @@ class _NotificationJob {
class UploadNotificationService {
static const MethodChannel _channel = MethodChannel(
'ru.komet.app/upload_service',
'ru.qlyra.app/upload_service',
);
static const int _minIntervalMs = 350;
static const Duration _startDelay = Duration(milliseconds: 700);
@@ -189,7 +189,7 @@ class UploadNotificationService {
}
static AppLocalizations _localizations() {
final context = KometApp.navigatorKey.currentContext;
final context = QlyraApp.navigatorKey.currentContext;
if (context != null) {
final scoped = Localizations.of<AppLocalizations>(
context,
+3 -1
View File
@@ -91,7 +91,9 @@ class WebAppModule {
throw const WebAppUnavailable('Нет соединения с сервером');
}
final normalizedStartParam =
(startParam != null && startParam.trim().isNotEmpty) ? startParam : null;
(startParam != null && startParam.trim().isNotEmpty)
? startParam
: null;
final packet = await _api.sendRequest(Opcode.webAppInitData, {
'botId': botId,
'startParam': ?normalizedStartParam,
+1 -2
View File
@@ -4,8 +4,7 @@ class SelfPresence {
static final ValueNotifier<bool> isOnline = ValueNotifier(true);
static final ValueNotifier<int?> lastSeenSeconds = ValueNotifier(null);
static int get _nowSeconds =>
DateTime.now().millisecondsSinceEpoch ~/ 1000;
static int get _nowSeconds => DateTime.now().millisecondsSinceEpoch ~/ 1000;
static void markOnline() {
isOnline.value = true;
+2 -2
View File
@@ -11,8 +11,8 @@ class CallBridge {
CallBridge._();
static final CallBridge instance = CallBridge._();
static const _method = MethodChannel('ru.komet.app/calls');
static const _events = EventChannel('ru.komet.app/calls_events');
static const _method = MethodChannel('ru.qlyra.app/calls');
static const _events = EventChannel('ru.qlyra.app/calls_events');
bool _started = false;
+4 -8
View File
@@ -204,14 +204,10 @@ class CallController {
params: call.params,
role: CallRole.callee,
);
return _launch(
session,
() async {
await session.start();
await session.accept();
},
caller: call.callerName,
);
return _launch(session, () async {
await session.start();
await session.accept();
}, caller: call.callerName);
}
Future<void> rejectIncoming(IncomingCall call) async {
+21 -21
View File
@@ -133,7 +133,7 @@ class CallSession {
static const int _speakHoldTicks = 3;
RTCDataChannel? _probeChannel;
bool _peerIsKomet = false;
bool _peerIsQlyra = false;
final List<RTCDataChannel> _sfuChannels = [];
SfuCommandChannel? _sfuCommands;
@@ -155,9 +155,9 @@ class CallSession {
'producerNotification',
];
static const bool _kometProbeEnabled = false;
static const String _probeQuestion = 'AreYouKomet?';
static const String _probeAnswer = 'YesImKomet😎';
static const bool _qlyraProbeEnabled = false;
static const String _probeQuestion = 'AreYouQlyra?';
static const String _probeAnswer = 'YesImQlyra😎';
final List<CallChatMessage> _chat = [];
final _chatController = StreamController<CallChatMessage>.broadcast();
@@ -206,7 +206,7 @@ class CallSession {
final _state = StreamController<CallSessionState>.broadcast();
final _remoteStream = StreamController<MediaStream>.broadcast();
final _info = StreamController<void>.broadcast();
final _kometDetected = StreamController<void>.broadcast();
final _qlyraDetected = StreamController<void>.broadcast();
Stream<CallSessionState> get stateStream => _state.stream;
Stream<MediaStream> get remoteStreamStream => _remoteStream.stream;
@@ -214,8 +214,8 @@ class CallSession {
Stream<void> get infoUpdates => _info.stream;
Stream<void> get peerKometDetected => _kometDetected.stream;
bool get peerIsKomet => _peerIsKomet;
Stream<void> get peerQlyraDetected => _qlyraDetected.stream;
bool get peerIsQlyra => _peerIsQlyra;
bool get isMuted => _muted;
bool get audioTransmitting => !_muted || CallNoMute.enabled;
@@ -814,7 +814,7 @@ class CallSession {
init: RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly),
);
await _setupKometProbe(pc);
await _setupQlyraProbe(pc);
if (_isDesktop) await _preferVp8Codecs(pc);
@@ -846,7 +846,7 @@ class CallSession {
};
pc.onTrack = (event) => unawaited(_onRemoteTrack(event));
pc.onDataChannel = (channel) {
if (!_kometProbeEnabled) return;
if (!_qlyraProbeEnabled) return;
_bindProbeChannel(channel, ask: false);
};
pc.onIceConnectionState = (s) {
@@ -1252,11 +1252,11 @@ class CallSession {
}
}
Future<void> _setupKometProbe(RTCPeerConnection pc) async {
if (!_kometProbeEnabled || _topology == 'SERVER') return;
Future<void> _setupQlyraProbe(RTCPeerConnection pc) async {
if (!_qlyraProbeEnabled || _topology == 'SERVER') return;
try {
final channel = await pc.createDataChannel(
'komet',
'qlyra',
RTCDataChannelInit()..ordered = true,
);
_probeChannel = channel;
@@ -1296,7 +1296,7 @@ class CallSession {
if (text == _probeQuestion) {
_sendProbe(channel, _probeAnswer);
} else if (text == _probeAnswer) {
_markPeerKomet();
_markPeerQlyra();
}
}
@@ -1342,11 +1342,11 @@ class CallSession {
if (!_chatController.isClosed) _chatController.add(message);
}
void _markPeerKomet() {
if (_peerIsKomet) return;
_peerIsKomet = true;
logger.t('[call] peer is Komet');
if (!_kometDetected.isClosed) _kometDetected.add(null);
void _markPeerQlyra() {
if (_peerIsQlyra) return;
_peerIsQlyra = true;
logger.t('[call] peer is Qlyra');
if (!_qlyraDetected.isClosed) _qlyraDetected.add(null);
_notifyInfo();
}
@@ -1860,7 +1860,7 @@ class CallSession {
if (id == null || id == ws2Config.userId) return;
var stream = _participantStreams[id];
if (stream == null) {
stream = await createLocalMediaStream('komet_p$id');
stream = await createLocalMediaStream('qlyra_p$id');
_participantStreams[id] = stream;
}
if (stream.getTracks().any((t) => t.id == track.id)) return;
@@ -1876,7 +1876,7 @@ class CallSession {
Future<void> _pushRemoteTrack(MediaStreamTrack track) async {
var stream = _remoteStreamRef;
if (stream == null) {
stream = await createLocalMediaStream('komet_remote');
stream = await createLocalMediaStream('qlyra_remote');
_ownRemoteStream = true;
}
_remoteStreamRef = stream;
@@ -2349,7 +2349,7 @@ class CallSession {
if (!_state.isClosed) await _state.close();
if (!_remoteStream.isClosed) await _remoteStream.close();
if (!_info.isClosed) await _info.close();
if (!_kometDetected.isClosed) await _kometDetected.close();
if (!_qlyraDetected.isClosed) await _qlyraDetected.close();
if (!_chatController.isClosed) await _chatController.close();
if (!_gameController.isClosed) await _gameController.close();
}
+3 -3
View File
@@ -1,4 +1,4 @@
import 'package:kolibri/kolibri.dart' as kb;
import 'package:kusoft/kusoft.dart' as kb;
/// Параметры подключения к звонку (`vcp`), которые сервер присылает в пуше
/// входящего звонка (opcode 137) и в ответе на инициацию исходящего.
@@ -76,8 +76,8 @@ class ConversationParams {
return nowSec >= expiresAt! - 5;
}
/// Распаковывает и парсит строку `vcp` через Rust-ядро (kolibri). Возвращает
/// `null`, если формат не распознан. Требует инициализации `initKolibri()`.
/// Распаковывает и парсит строку `vcp` через Rust-ядро (kusoft). Возвращает
/// `null`, если формат не распознан. Требует инициализации `initKusoft()`.
static ConversationParams? decode(String vcp) {
final kb.CallParams? p = kb.decodeVcp(vcp: vcp, conversationId: '');
if (p == null) return null;
+1 -1
View File
@@ -29,7 +29,7 @@ class PulseRouteException implements Exception {
class PulseAudio {
PulseAudio._();
static const String bridgePrefix = 'komet_capture_';
static const String bridgePrefix = 'qlyra_capture_';
static String? _bridgeModule;
static String? _bridgeMaster;
+2 -2
View File
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:kolibri/kolibri.dart' as kb;
import 'package:kusoft/kusoft.dart' as kb;
import 'conversation_params.dart';
@@ -92,7 +92,7 @@ class Ws2CommandException implements Exception {
/// Клиент сигналинга звонка поверх WebSocket `ws2`.
///
/// Тонкий адаптер над Rust-ядром (kolibri [kb.CallSignaling]): ядро держит
/// Тонкий адаптер над Rust-ядром (kusoft [kb.CallSignaling]): ядро держит
/// WebSocket, корреляцию `sequence`/`response`, keepalive `ping`→`pong` и
/// разбор кадров; здесь — прежний Dart-интерфейс для [call_session].
///
+2 -5
View File
@@ -30,9 +30,6 @@ class AppComposerBackground {
static Future<void> save(ComposerBackground value) => _setting.save(value);
static ComposerBackground _parse(String? val) => enumFromName(
ComposerBackground.values,
val,
ComposerBackground.standard,
);
static ComposerBackground _parse(String? val) =>
enumFromName(ComposerBackground.values, val, ComposerBackground.standard);
}
+2 -2
View File
@@ -8,7 +8,7 @@ enum AppIcon {
defaultIcon(
'default',
'Default',
'assets/komet_icon.png',
'assets/qlyra_icon.png',
'MainActivity',
null,
),
@@ -37,7 +37,7 @@ enum AppIcon {
class AppIconConfig {
static const prefKey = 'app_icon';
static const _channel = MethodChannel('ru.komet.app/app_icon');
static const _channel = MethodChannel('ru.qlyra.app/app_icon');
static final ValueNotifier<AppIcon> current = ValueNotifier(
AppIcon.defaultIcon,
@@ -1,15 +1,15 @@
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class KometSettings {
static const _kViewDeleted = 'komet_view_deleted';
static const _kViewRedacted = 'komet_view_redacted';
static const _kFullTimestamp = 'komet_full_timestamp';
static const _kGhostMode = 'komet_ghost_mode';
static const _kAntiRead = 'komet_anti_read';
static const _kSelfOnlineCheck = 'komet_self_online_check';
static const _kHideAllChatsFolder = 'komet_hide_all_chats_folder';
static const _kShowHiddenChats = 'komet_show_hidden_chats';
class QlyraSettings {
static const _kViewDeleted = 'qlyra_view_deleted';
static const _kViewRedacted = 'qlyra_view_redacted';
static const _kFullTimestamp = 'qlyra_full_timestamp';
static const _kGhostMode = 'qlyra_ghost_mode';
static const _kAntiRead = 'qlyra_anti_read';
static const _kSelfOnlineCheck = 'qlyra_self_online_check';
static const _kHideAllChatsFolder = 'qlyra_hide_all_chats_folder';
static const _kShowHiddenChats = 'qlyra_show_hidden_chats';
static final ValueNotifier<bool> viewDeleted = ValueNotifier(false);
static final ValueNotifier<bool> viewRedacted = ValueNotifier(false);
+2 -2
View File
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:komet_crypto/komet_crypto.dart' as kc;
import 'package:qlyra_crypto/qlyra_crypto.dart' as kc;
import '../storage/chat_encryption_store.dart';
import '../utils/logger.dart';
@@ -47,7 +47,7 @@ class ChatCryptoService {
} catch (e) {
_init = null;
_unavailable = true;
logger.w('komet_crypto init failed: $e');
logger.w('qlyra_crypto init failed: $e');
return false;
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ Future<File?> reencodeAsPng(File source, String destPath) async {
}
Future<Directory> _scratchDir() async {
final dir = Directory('${(await getTemporaryDirectory()).path}/komet_enc');
final dir = Directory('${(await getTemporaryDirectory()).path}/qlyra_enc');
if (!await dir.exists()) await dir.create(recursive: true);
return dir;
}
+7 -11
View File
@@ -88,7 +88,7 @@ class DeepLinkService {
}
void _flushPending() {
final context = KometApp.navigatorKey.currentContext;
final context = QlyraApp.navigatorKey.currentContext;
if (_pendingLogExport) {
if (context == null) {
@@ -162,7 +162,8 @@ class DeepLinkService {
WebPushSubscription? _parseWebPushLink(Uri uri) {
if (!Platform.isIOS) return null;
if (uri.scheme.toLowerCase() != 'komet') return null;
final scheme = uri.scheme.toLowerCase();
if (scheme != 'qlyra') return null;
final segments = <String>[
if (uri.host.isNotEmpty) uri.host,
@@ -214,20 +215,15 @@ class DeepLinkService {
bool _isLogExportLink(Uri uri) {
final scheme = uri.scheme.toLowerCase();
final host = uri.host.toLowerCase();
final segments = <String>[
if (scheme == 'komet' && host.isNotEmpty) host,
if (scheme == 'qlyra' && uri.host.isNotEmpty)
uri.host,
...uri.pathSegments,
].where((s) => s.isNotEmpty).toList();
if (scheme == 'komet') {
if (scheme == 'qlyra') {
return segments.length == 1 && segments.first == 'export-logs';
}
if (scheme == 'https' || scheme == 'http') {
return (host == 'komet.pw' || host == 'www.komet.pw') &&
segments.length == 1 &&
segments.first == 'export-logs';
}
return false;
}
@@ -240,7 +236,7 @@ class DeepLinkService {
return null;
}
if (scheme == 'komet' || scheme == 'max') {
if (scheme == 'qlyra' || scheme == 'max') {
final segments = <String>[
if (uri.host.isNotEmpty && uri.host.toLowerCase() != 'max.ru') uri.host,
...uri.pathSegments,
+5 -4
View File
@@ -2,7 +2,7 @@ import 'dart:io';
import '../utils/logger.dart';
const List<String> _schemes = ['komet', 'max'];
const List<String> _schemes = ['qlyra', 'max'];
abstract class DesktopUrlScheme {
static Future<void> register() async {
@@ -44,11 +44,12 @@ abstract class DesktopUrlScheme {
final appsDir = Directory('$home/.local/share/applications');
await appsDir.create(recursive: true);
const fileName = 'komet-url-handler.desktop';
const fileName = 'qlyra-url-handler.desktop';
final mimeTypes = _schemes.map((s) => 'x-scheme-handler/$s').join(';');
final desktop = '[Desktop Entry]\n'
final desktop =
'[Desktop Entry]\n'
'Type=Application\n'
'Name=Komet\n'
'Name=Qlyra\n'
'Exec="$exe" %u\n'
'Terminal=false\n'
'NoDisplay=true\n'
+3 -1
View File
@@ -104,7 +104,9 @@ class DominantColorCache {
final darkest = red < green
? (red < blue ? red : blue)
: (green < blue ? green : blue);
final saturation = brightest <= 0 ? 0.0 : (brightest - darkest) / brightest;
final saturation = brightest <= 0
? 0.0
: (brightest - darkest) / brightest;
final weight = (alpha / 255) * (_achromaticFloor + saturation);
accumulatedRed += red * weight;
+1 -1
View File
@@ -88,7 +88,7 @@ Future<File> writePhotoJpeg(Uint8List bytes) async {
final file = File(
p.join(
dir.path,
'komet_photo_${DateTime.now().microsecondsSinceEpoch}.jpg',
'qlyra_photo_${DateTime.now().microsecondsSinceEpoch}.jpg',
),
);
await file.writeAsBytes(bytes, flush: true);
@@ -16,7 +16,7 @@ class VideoNoteAccess {
}
class NativeVideoNoteRecorder {
static const _channel = MethodChannel('ru.komet.app/video_note');
static const _channel = MethodChannel('ru.qlyra.app/video_note');
int? textureId;
bool hasFlash = false;
+5 -1
View File
@@ -92,7 +92,11 @@ class OpusOggIndex {
pendingContiguous = true;
} else if (pendingLength > 0 && pendingContiguous) {
pendingParts.add(
Uint8List.sublistView(bytes, pendingStart, pendingStart + pendingLength),
Uint8List.sublistView(
bytes,
pendingStart,
pendingStart + pendingLength,
),
);
pendingContiguous = false;
}
+1 -1
View File
@@ -26,7 +26,7 @@ Future<File?> rasterPictureToJpegFile(
final out = File(
p.join(
dir.path,
'komet_${prefix}_${DateTime.now().microsecondsSinceEpoch}.jpg',
'qlyra_${prefix}_${DateTime.now().microsecondsSinceEpoch}.jpg',
),
);
await out.writeAsBytes(jpeg);
+1 -2
View File
@@ -1,2 +1 @@
export 'rlottie_engine_stub.dart'
if (dart.library.io) 'rlottie_engine.dart';
export 'rlottie_engine_stub.dart' if (dart.library.io) 'rlottie_engine.dart';
+10 -3
View File
@@ -81,7 +81,13 @@ class RlottieDiskCache {
}) async {
if (frames.isEmpty || frames.length != frameCount) return;
try {
final bytes = await _encode(px, frameCount, frameRate, durationMs, frames);
final bytes = await _encode(
px,
frameCount,
frameRate,
durationMs,
frames,
);
final file = await _file(url, px);
await file.writeAsBytes(bytes, flush: false);
unawaited(_evict());
@@ -147,8 +153,9 @@ class RlottieDiskCache {
}
final frames = <Uint8List>[];
for (var i = 0; i < frameCount; i++) {
frames.add(Uint8List.sublistView(
payload, i * frameBytes, (i + 1) * frameBytes));
frames.add(
Uint8List.sublistView(payload, i * frameBytes, (i + 1) * frameBytes),
);
}
return DiskClip(
px: px,
+30 -17
View File
@@ -180,14 +180,16 @@ class RlottieEngine {
final clip = job.clip..complete = true;
final raw = job.rawFrames;
if (raw.length == clip.frameCount && !raw.contains(null)) {
unawaited(RlottieDiskCache.instance.store(
url: job.url,
px: clip.px,
frameCount: clip.frameCount,
frameRate: clip.frameRate,
durationMs: clip.durationMs,
frames: raw.cast<Uint8List>(),
));
unawaited(
RlottieDiskCache.instance.store(
url: job.url,
px: clip.px,
frameCount: clip.frameCount,
frameRate: clip.frameRate,
durationMs: clip.durationMs,
frames: raw.cast<Uint8List>(),
),
);
}
job.rawFrames = const [];
}
@@ -235,7 +237,11 @@ class RlottieEngine {
}
Future<RlottieClip?> _load(
String url, int px, String key, String? inlineJson) async {
String url,
int px,
String key,
String? inlineJson,
) async {
if (inlineJson == null) {
final disk = await RlottieDiskCache.instance.load(url, px);
if (disk != null) {
@@ -260,13 +266,15 @@ class RlottieEngine {
_jobs[jobId] = _Job(clip, url, completer);
final port = await _worker();
port.send(RenderJob(
jobId: jobId,
json: json,
cacheKey: url,
px: px,
libPath: debugLibraryPath,
));
port.send(
RenderJob(
jobId: jobId,
json: json,
cacheKey: url,
px: px,
libPath: debugLibraryPath,
),
);
return completer.future;
}
@@ -288,7 +296,12 @@ class RlottieEngine {
Future<ui.Image> _decode(Uint8List bgra, int px) {
final completer = Completer<ui.Image>();
ui.decodeImageFromPixels(
bgra, px, px, ui.PixelFormat.bgra8888, completer.complete);
bgra,
px,
px,
ui.PixelFormat.bgra8888,
completer.complete,
);
return completer.future;
}
@@ -22,8 +22,11 @@ class RlottieEngine {
bool get available => false;
Future<RlottieClip?> acquire(String url, int px, {String? inlineJson}) async =>
null;
Future<RlottieClip?> acquire(
String url,
int px, {
String? inlineJson,
}) async => null;
Future<void> prewarm(String url, int px) async {}
+22 -15
View File
@@ -6,8 +6,8 @@ import 'package:ffi/ffi.dart';
typedef _InitNative = Void Function();
typedef _VoidFn = void Function();
typedef _FromDataNative = Pointer<Void> Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>);
typedef _FromDataNative =
Pointer<Void> Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>);
typedef _SizeGetterNative = Size Function(Pointer<Void>);
typedef _SizeGetter = int Function(Pointer<Void>);
@@ -15,10 +15,10 @@ typedef _SizeGetter = int Function(Pointer<Void>);
typedef _DoubleGetterNative = Double Function(Pointer<Void>);
typedef _DoubleGetter = double Function(Pointer<Void>);
typedef _RenderNative = Void Function(
Pointer<Void>, Size, Pointer<Uint32>, Size, Size, Size);
typedef _Render = void Function(
Pointer<Void>, int, Pointer<Uint32>, int, int, int);
typedef _RenderNative =
Void Function(Pointer<Void>, Size, Pointer<Uint32>, Size, Size, Size);
typedef _Render =
void Function(Pointer<Void>, int, Pointer<Uint32>, int, int, int);
typedef _DestroyNative = Void Function(Pointer<Void>);
typedef _Destroy = void Function(Pointer<Void>);
@@ -31,19 +31,26 @@ class RlottieBindings {
_init = _lib.lookupFunction<_InitNative, _VoidFn>('lottie_init');
_shutdown = _lib.lookupFunction<_InitNative, _VoidFn>('lottie_shutdown');
_fromData = _lib.lookupFunction<_FromDataNative, _FromDataNative>(
'lottie_animation_from_data');
'lottie_animation_from_data',
);
_totalFrame = _lib.lookupFunction<_SizeGetterNative, _SizeGetter>(
'lottie_animation_get_totalframe');
'lottie_animation_get_totalframe',
);
_frameRate = _lib.lookupFunction<_DoubleGetterNative, _DoubleGetter>(
'lottie_animation_get_framerate');
'lottie_animation_get_framerate',
);
_duration = _lib.lookupFunction<_DoubleGetterNative, _DoubleGetter>(
'lottie_animation_get_duration');
_render =
_lib.lookupFunction<_RenderNative, _Render>('lottie_animation_render');
_destroy = _lib
.lookupFunction<_DestroyNative, _Destroy>('lottie_animation_destroy');
'lottie_animation_get_duration',
);
_render = _lib.lookupFunction<_RenderNative, _Render>(
'lottie_animation_render',
);
_destroy = _lib.lookupFunction<_DestroyNative, _Destroy>(
'lottie_animation_destroy',
);
_cacheSize = _lib.lookupFunction<_CacheSizeNative, _CacheSize>(
'lottie_configure_model_cache_size');
'lottie_configure_model_cache_size',
);
_init();
}
+18 -13
View File
@@ -112,12 +112,14 @@ void rlottieWorkerMain(SendPort toMain) {
outCount = (durationMs / 1000.0 * _maxCacheFps).round().clamp(2, total);
}
final outFps = durationMs <= 0 ? fps : outCount * 1000.0 / durationMs;
toMain.send(ClipMeta(
jobId: job.jobId,
totalFrame: outCount,
frameRate: outFps,
durationMs: durationMs,
));
toMain.send(
ClipMeta(
jobId: job.jobId,
totalFrame: outCount,
frameRate: outFps,
durationMs: durationMs,
),
);
final px = job.px;
final buffer = calloc<Uint32>(px * px);
@@ -129,12 +131,16 @@ void rlottieWorkerMain(SendPort toMain) {
? i
: (i * (total - 1) / (outCount - 1)).round().clamp(0, total - 1);
rl.render(anim, src, buffer, px);
toMain.send(RenderedFrame(
jobId: job.jobId,
index: i,
data: TransferableTypedData.fromList([Uint8List.fromList(byteView)]),
px: px,
));
toMain.send(
RenderedFrame(
jobId: job.jobId,
index: i,
data: TransferableTypedData.fromList([
Uint8List.fromList(byteView),
]),
px: px,
),
);
}
} finally {
calloc.free(buffer);
@@ -148,4 +154,3 @@ void rlottieWorkerMain(SendPort toMain) {
}
});
}
+1 -1
View File
@@ -9,7 +9,7 @@ import '../utils/logger.dart';
/// AVAssetExportSession. Без искажений: заполняет квадрат и обрезает
/// лишнее по бокам. На других платформах возвращает `null`.
class VideoNoteCropper {
static const _channel = MethodChannel('ru.komet.app/video');
static const _channel = MethodChannel('ru.qlyra.app/video');
static Future<String?> cropSquare(String input, {int size = 480}) async {
if (!Platform.isAndroid && !Platform.isIOS) return null;
+3 -3
View File
@@ -66,7 +66,7 @@ class VideoExportSpec {
}
class VideoTranscoder {
static const _channel = MethodChannel('ru.komet.app/video');
static const _channel = MethodChannel('ru.qlyra.app/video');
static bool get _native => Platform.isAndroid || Platform.isIOS;
@@ -143,7 +143,7 @@ class VideoTranscoder {
return File(
p.join(
dir.path,
'komet_${prefix}_${DateTime.now().microsecondsSinceEpoch}.mp4',
'qlyra_${prefix}_${DateTime.now().microsecondsSinceEpoch}.mp4',
),
);
}
@@ -356,7 +356,7 @@ class VideoTranscoder {
final file = File(
p.join(
dir.path,
'komet_lut_${DateTime.now().microsecondsSinceEpoch}.cube',
'qlyra_lut_${DateTime.now().microsecondsSinceEpoch}.cube',
),
);
await file.writeAsString(buffer.toString());
+2 -2
View File
@@ -29,8 +29,8 @@ class NfcExchangeService {
NfcExchangeService._();
static final NfcExchangeService instance = NfcExchangeService._();
static const MethodChannel _method = MethodChannel('ru.komet.app/nfc');
static const EventChannel _events = EventChannel('ru.komet.app/nfc_events');
static const MethodChannel _method = MethodChannel('ru.qlyra.app/nfc');
static const EventChannel _events = EventChannel('ru.qlyra.app/nfc_events');
bool get _supported => Platform.isAndroid;
+4 -2
View File
@@ -109,7 +109,8 @@ abstract class Opcode {
static const int chatMembersUpdate = 77; // Обновление участников / добавление
static const int videoChatStartActive = 78; // Инициация активного звонка
static const int videoChatHistory = 79; // История звонков
static const int videoChatDeleteHistory = 164; // Удаление записей истории звонков
static const int videoChatDeleteHistory =
164; // Удаление записей истории звонков
static const int videoChatCreateJoinLink = 84; // Ссылка для входа в видеочат
static const int videoChatJoinByLink = 166; // Вход в звонок по ссылке
static const int videoChatMembers = 195; // Участники видеочата
@@ -129,7 +130,8 @@ abstract class Opcode {
// ── Comments (комментарии к постам каналов) ────────────────────────
// Загрузка/отправка/набор комментариев переиспользуют chatHistory (49),
// msgSend (64) и msgTyping (65) с добавленным полем postId.
static const int commentsInfo = 91; // Кол-во комментариев к постам (totalCount)
static const int commentsInfo =
91; // Кол-во комментариев к постам (totalCount)
// ── Sessions ───────────────────────────────────────────────────────
static const int sessionsInfo = 96; // Запрос активных сессий
+1 -1
View File
@@ -11,7 +11,7 @@ abstract class CmdType {
/// Распакованный пакет.
///
/// Провод (фрейминг, MsgPack, сжатие) живёт в Rust-ядре kolibri; здесь пакет —
/// Провод (фрейминг, MsgPack, сжатие) живёт в Rust-ядре kusoft; здесь пакет —
/// это уже декодированный [payload] (Map/List/скаляр, бинарь — Uint8List) плюс
/// метаданные заголовка.
class Packet {
+2 -2
View File
@@ -4,12 +4,12 @@ import 'package:flutter/services.dart';
import '../utils/logger.dart';
/// Канал к нативному сервису FKM (foreground komet messaging).
/// Канал к нативному сервису FKM (foreground qlyra messaging).
class FkmBridge {
FkmBridge._();
static final FkmBridge instance = FkmBridge._();
static const _method = MethodChannel('ru.komet.app/fkm');
static const _method = MethodChannel('ru.qlyra.app/fkm');
VoidCallback? _onDisabled;
bool _handlerSet = false;
+5 -6
View File
@@ -11,7 +11,7 @@ import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
import '../../core/storage/app_database.dart';
import '../../core/storage/token_storage.dart';
import '../config/komet_settings.dart';
import '../config/qlyra_settings.dart';
import '../utils/logger.dart';
import 'fkm_bridge.dart';
import 'push_service.dart';
@@ -22,7 +22,7 @@ const _hiddenPreview = 'Новое сообщение';
/// FKM — уведомления через собственное фоновое соединение, без FCM.
///
/// Пуш из сокета превращается в тот же набор полей, что присылает FCM, и
/// отрисовывается нативным `KometNotifier` — общий код с пушевой версией.
/// отрисовывается нативным `QlyraNotifier` — общий код с пушевой версией.
class FkmController {
FkmController._();
static final FkmController instance = FkmController._();
@@ -80,9 +80,8 @@ class FkmController {
unawaited(FkmBridge.instance.setConnected(state == SessionState.online));
}
Future<void> _pushConnectionState() => FkmBridge.instance.setConnected(
_api?.state == SessionState.online,
);
Future<void> _pushConnectionState() =>
FkmBridge.instance.setConnected(_api?.state == SessionState.online);
/// Входящий звонок, когда приложение не на переднем плане.
///
@@ -196,7 +195,7 @@ class FkmController {
FkmBridge.instance.removeMessage({
'mc': '$chatId',
'msgid': msgId,
'keep': KometSettings.viewDeleted.value ? 'true' : 'false',
'keep': QlyraSettings.viewDeleted.value ? 'true' : 'false',
});
Future<void> _editNotification(
+3 -3
View File
@@ -12,8 +12,8 @@ class NotificationBridge {
NotificationBridge._();
static final NotificationBridge instance = NotificationBridge._();
static const _method = MethodChannel('ru.komet.app/notifications');
static const _events = EventChannel('ru.komet.app/notification_events');
static const _method = MethodChannel('ru.qlyra.app/notifications');
static const _events = EventChannel('ru.qlyra.app/notification_events');
static const _retryDelay = Duration(milliseconds: 300);
static const _maxRetries = 100;
@@ -92,7 +92,7 @@ class NotificationBridge {
final chatId = _pendingChatId;
if (chatId <= 0) return;
final context = KometApp.navigatorKey.currentContext;
final context = QlyraApp.navigatorKey.currentContext;
if (!_ready || context == null || api.state != SessionState.online) {
if (_retriesLeft <= 0) {
_pendingChatId = 0;
+5 -5
View File
@@ -5,7 +5,7 @@ import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:kolibri/kolibri.dart' show initKolibri;
import 'package:kusoft/kusoft.dart' show initKusoft;
import 'package:shared_preferences/shared_preferences.dart';
import '../../backend/api.dart';
@@ -19,7 +19,7 @@ import '../storage/token_storage.dart';
import '../transport/tls_config.dart';
import '../utils/logger.dart';
const _channelId = 'komet_messages';
const _channelId = 'qlyra_messages';
const _channelName = 'Сообщения';
const _prefsTokenKey = 'fcm_push_token';
@@ -56,7 +56,7 @@ Future<void> _handleCallDecline(String payloadJson) async {
if (vcp.isEmpty || conversationId.isEmpty) return;
// Фоновый изолят: инициализируем ядро перед vcp-декодом/сигналингом.
await initKolibri();
await initKusoft();
await TlsConfig.applyMincifryTrust();
final params = ConversationParams.decode(vcp);
@@ -89,7 +89,7 @@ Future<void> _handleReply(String payloadJson, String text) async {
if (account == 0 || chatId == 0) return;
WidgetsFlutterBinding.ensureInitialized();
await initKolibri();
await initKusoft();
if (AppInstance.isNamed) {
try {
SharedPreferences.setPrefix('flutter.${AppInstance.id}.');
@@ -134,7 +134,7 @@ Future<void> _handleReply(String payloadJson, String text) async {
} else {
await plugin.show(
id: notifId,
title: 'Komet',
title: 'Qlyra',
body: 'Не удалось отправить ответ',
notificationDetails: const NotificationDetails(
android: AndroidNotificationDetails(
+3 -3
View File
@@ -14,8 +14,8 @@ class ShareIntentBridge {
ShareIntentBridge._();
static final ShareIntentBridge instance = ShareIntentBridge._();
static const _method = MethodChannel('ru.komet.app/share');
static const _events = EventChannel('ru.komet.app/share_events');
static const _method = MethodChannel('ru.qlyra.app/share');
static const _events = EventChannel('ru.qlyra.app/share_events');
static const _retryDelay = Duration(milliseconds: 300);
static const _maxRetries = 100;
@@ -85,7 +85,7 @@ class ShareIntentBridge {
final payload = _pending;
if (payload == null || _presenting) return;
final context = KometApp.navigatorKey.currentContext;
final context = QlyraApp.navigatorKey.currentContext;
if (!_ready || context == null || api.state != SessionState.online) {
if (_retriesLeft <= 0) {
_pending = null;
+5 -5
View File
@@ -2,8 +2,8 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:komet/core/storage/app_instance.dart';
import 'package:komet/core/utils/logger.dart';
import 'package:qlyra/core/storage/app_instance.dart';
import 'package:qlyra/core/utils/logger.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart' show databaseFactorySqflitePlugin;
@@ -203,11 +203,11 @@ class AppDatabase {
if (!(Platform.isLinux || Platform.isWindows || Platform.isMacOS)) return;
try {
if (await File(target).exists()) return;
final legacy = File(join(await getDatabasesPath(), 'komet.db'));
final legacy = File(join(await getDatabasesPath(), 'qlyra.db'));
if (legacy.path == target) return;
if (await legacy.exists()) {
await legacy.copy(target);
logger.i('[db] перенёс komet.db -> $target');
logger.i('[db] перенёс qlyra.db -> $target');
}
} catch (e) {
logger.w('legacy db migration failed: $e');
@@ -217,7 +217,7 @@ class AppDatabase {
static Future<Database> _open() async {
final dbPath = await _databasesDir();
await Directory(dbPath).create(recursive: true);
final target = join(dbPath, 'komet${AppInstance.suffix}.db');
final target = join(dbPath, 'qlyra${AppInstance.suffix}.db');
await _migrateLegacyDb(target);
return openDatabase(
target,
+1 -1
View File
@@ -1,7 +1,7 @@
class AppInstance {
AppInstance._();
static const String id = String.fromEnvironment('KOMET_INSTANCE');
static const String id = String.fromEnvironment('QLYRA_INSTANCE');
static bool get isNamed => id.isNotEmpty;
+1 -2
View File
@@ -10,8 +10,7 @@ class ArchivedChatsStore extends PerChatJsonStore<bool> {
static final ArchivedChatsStore instance = ArchivedChatsStore._();
bool isArchived(int accountId, int chatId) =>
read(accountId, chatId) == true;
bool isArchived(int accountId, int chatId) => read(accountId, chatId) == true;
Future<void> setArchived(int accountId, int chatId, bool archived) =>
write(accountId, chatId, archived ? true : null);
-1
View File
@@ -31,5 +31,4 @@ abstract class DeviceIdentity {
await prefs.setString(_deviceIdKey, generated);
return generated;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:kolibri/kolibri.dart' show setTrustMincifryCa;
import 'package:kusoft/kusoft.dart' show setTrustMincifryCa;
import 'package:shared_preferences/shared_preferences.dart';
import '../config/config.dart';
+1 -1
View File
@@ -145,7 +145,7 @@ class TrafficMonitor extends ChangeNotifier {
/// маскируются через [redactForLog] — файлом можно делиться.
String buildExport({String? appVersion}) {
final data = <String, dynamic>{
'tool': 'Komet traffic monitor',
'tool': 'Qlyra traffic monitor',
'appVersion': ?appVersion,
'exportedAt': DateTime.now().toIso8601String(),
'endpoint': _activeEndpoint,
+1 -1
View File
@@ -38,7 +38,7 @@ class VpnBypassService {
static const String prefKey = 'dev_vpn_bypass';
static const MethodChannel _channel = MethodChannel(
'ru.komet.app/vpn_bypass',
'ru.qlyra.app/vpn_bypass',
);
bool _bound = false;
+1 -1
View File
@@ -309,7 +309,7 @@ class DebugSessionLog {
if (totalEntries == 0 && totalLogs == 0) return null;
final info = StringBuffer();
info.writeln('Komet — отладочный лог');
info.writeln('Qlyra — отладочный лог');
if (endpoint != null) info.writeln('Сервер: $endpoint');
info.writeln('Экспортирован: ${DateTime.now().toIso8601String()}');
info.writeln('Период: последние ${_retention.inHours} часа');
+1 -2
View File
@@ -32,8 +32,7 @@ class EmojiKeywordIndex {
});
}
List<String> get all =>
List.unmodifiable(_entries.map((e) => e.emoji));
List<String> get all => List.unmodifiable(_entries.map((e) => e.emoji));
List<String> search(String query) {
final targets = resolve(query);
+1 -1
View File
@@ -1,7 +1,7 @@
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Centralized tactile feedback for Komet.
/// Centralized tactile feedback for Qlyra.
///
/// Wraps Flutter's [HapticFeedback] so the whole app speaks one tactile
/// "language": the same gesture always feels the same. Composite patterns
+5 -2
View File
@@ -7,7 +7,8 @@ const int _avatarTargetBytes = 900 * 1024;
/// Maximum accepted size for a user-picked avatar before compression.
const int kMaxAvatarBytes = 8 * 1024 * 1024;
Future<Uint8List?> compressAvatar(Uint8List input) => compute(_encodeAvatar, input);
Future<Uint8List?> compressAvatar(Uint8List input) =>
compute(_encodeAvatar, input);
Future<Uint8List?> encodeRgbaToJpeg(Uint8List rgba, int width, int height) =>
compute(_encodeRgba, (rgba, width, height));
@@ -29,7 +30,9 @@ Uint8List? _encodeAvatar(Uint8List input) {
final decoded = img.decodeImage(input);
if (decoded == null) return null;
final oriented = img.bakeOrientation(decoded);
final image = oriented.width > _avatarMaxDimension || oriented.height > _avatarMaxDimension
final image =
oriented.width > _avatarMaxDimension ||
oriented.height > _avatarMaxDimension
? img.copyResize(
oriented,
width: oriented.width >= oriented.height ? _avatarMaxDimension : null,
+1 -1
View File
@@ -23,7 +23,7 @@ bool leavesWebView(String? scheme) {
return !_webViewSchemes.contains(scheme.toLowerCase());
}
const Set<String> _appSchemes = {'komet', 'max'};
const Set<String> _appSchemes = {'qlyra', 'max'};
Future<void> openExternalUrl(BuildContext context, String url) async {
final appUri = Uri.tryParse(url.trim());
+4 -4
View File
@@ -6,7 +6,7 @@ import 'package:logger/logger.dart';
import 'debug_session_log.dart';
Level _minimumLogLevel() {
const raw = String.fromEnvironment('KOMET_LOG_LEVEL', defaultValue: '');
const raw = String.fromEnvironment('QLYRA_LOG_LEVEL', defaultValue: '');
switch (raw.toLowerCase()) {
case 'trace':
return Level.trace;
@@ -42,7 +42,7 @@ LogFilter _logFilter() {
final logger = Logger(
filter: _logFilter(),
level: _minimumLogLevel(),
printer: KometLogPrinter(),
printer: QlyraLogPrinter(),
output: MultiOutput([ConsoleOutput(), DebugSessionLogOutput()]),
);
@@ -115,8 +115,8 @@ AnsiColor _levelColor(Level level) {
return AnsiColor.fg(AnsiColor.grey(0.5));
}
class KometLogPrinter extends LogPrinter {
KometLogPrinter({this.colors = true});
class QlyraLogPrinter extends LogPrinter {
QlyraLogPrinter({this.colors = true});
final bool colors;
+1 -1
View File
@@ -9,7 +9,7 @@ class ScreenWake {
static final ScreenWake instance = ScreenWake._();
static const _channel = MethodChannel('ru.komet.app/screen');
static const _channel = MethodChannel('ru.qlyra.app/screen');
final Set<Object> _holders = <Object>{};
+4 -1
View File
@@ -54,7 +54,10 @@ class _TiledSvgPatternState extends State<TiledSvgPattern> {
if (_image != cached) setState(() => _image = cached);
return;
}
final future = _pending.putIfAbsent(key, () => _rasterize(widget.asset, px));
final future = _pending.putIfAbsent(
key,
() => _rasterize(widget.asset, px),
);
try {
final image = await future;
_cache[key] = image;
+26 -21
View File
@@ -39,9 +39,10 @@ class UpdateCheckResult {
}
abstract class UpdateChecker {
static const String _owner = 'KometTeam';
static const String _repo = 'Komet';
static const String _userAgent = 'KometUpdateChecker';
static const String _userAgent = 'QlyraUpdateChecker';
static const String _baseUrl = 'https://argus.kusoft.xyz';
static const String _manifestUrl =
'$_baseUrl/api/apps/qlyra/manifest?platform=android&channel=stable';
static const String _lastCheckKey = 'update_last_check_ms';
static const String _skippedTagKey = 'update_skipped_tag';
@@ -132,41 +133,45 @@ abstract class UpdateChecker {
await prefs.setString(_skippedTagKey, tag);
}
static String get _releasesPage =>
'https://github.com/$_owner/$_repo/releases';
static String get _releasesPage => _manifestUrl;
static Future<Map<String, dynamic>?> _fetchLatestRelease() async {
final uri = Uri.parse(
'https://api.github.com/repos/$_owner/$_repo/releases?per_page=10',
);
final uri = Uri.parse(_manifestUrl);
final client = HttpClient()..connectionTimeout = _timeout;
try {
final req = await client.getUrl(uri);
req.headers
..set(HttpHeaders.userAgentHeader, _userAgent)
..set(HttpHeaders.acceptHeader, 'application/vnd.github+json');
..set(HttpHeaders.acceptHeader, 'application/json');
final resp = await req.close().timeout(_timeout);
if (resp.statusCode != HttpStatus.ok) {
await resp.drain<void>();
throw HttpException(
'GitHub returned HTTP ${resp.statusCode}',
uri: uri,
);
throw HttpException('Argus returned HTTP ${resp.statusCode}', uri: uri);
}
final body = await resp
.transform(const Utf8Decoder())
.join()
.timeout(_timeout);
final decoded = jsonDecode(body);
if (decoded is! List) {
throw const FormatException('Invalid GitHub releases response');
if (decoded is! Map || decoded['release'] is! Map) {
throw const FormatException('Invalid Argus manifest response');
}
for (final entry in decoded) {
if (entry is! Map) continue;
if (entry['draft'] == true) continue;
return entry.cast<String, dynamic>();
}
return null;
final release = (decoded['release'] as Map).cast<String, dynamic>();
final version = release['version'] as String?;
final downloadPath = release['downloadPath'] as String?;
if (version == null || downloadPath == null) return null;
final downloadUrl = Uri.parse(_baseUrl).resolve(downloadPath).toString();
return {
'tag_name': version,
'html_url': downloadUrl,
'body': release['notes'] as String? ?? '',
'assets': [
{
'name': 'qlyra-qlyra-universal.apk',
'browser_download_url': downloadUrl,
},
],
};
} finally {
client.close(force: true);
}
+5 -3
View File
@@ -57,7 +57,9 @@ abstract class UpdateInstaller {
if (!Platform.isAndroid || info.assets.isEmpty) return null;
final packageInfo = await PackageInfo.fromPlatform();
final flavor = packageInfo.packageName == 'ru.oneme.app' ? 'oneme' : 'komet';
final flavor = packageInfo.packageName == 'ru.oneme.app'
? 'oneme'
: 'qlyra';
final androidInfo = await DeviceInfoPlugin().androidInfo;
final abis = androidInfo.supportedAbis;
@@ -83,13 +85,13 @@ abstract class UpdateInstaller {
) async {
final dir = await getTemporaryDirectory();
final safeTag = tag.replaceAll(RegExp(r'[^A-Za-z0-9._-]'), '_');
final file = File('${dir.path}/komet-update-$safeTag.apk');
final file = File('${dir.path}/qlyra-update-$safeTag.apk');
final part = File('${file.path}.part');
final client = HttpClient();
try {
final request = await client.getUrl(Uri.parse(url));
request.headers.set(HttpHeaders.userAgentHeader, 'KometUpdateInstaller');
request.headers.set(HttpHeaders.userAgentHeader, 'QlyraUpdateInstaller');
final response = await request.close();
if (response.statusCode != HttpStatus.ok) {
await response.drain<void>();
+54 -24
View File
@@ -28,6 +28,8 @@ abstract class MaxWebCmd {
abstract class MaxWebFraming {
static const int protocolVersion = 10;
static const int headerSize = 10;
static const int maxPayloadSize = 0x00ffffff;
static const int maxDecompressedPayloadSize = 64 * 1024 * 1024;
static Uint8List encode({
required int cmd,
@@ -35,9 +37,14 @@ abstract class MaxWebFraming {
required int opcode,
Object? payload,
}) {
final body = payload == null
? Uint8List(0)
: MaxMsgpack.encode(payload);
final body = payload == null ? Uint8List(0) : MaxMsgpack.encode(payload);
if (body.length > maxPayloadSize) {
throw ArgumentError.value(
body.length,
'payload',
'превышает допустимый размер кадра',
);
}
final frame = Uint8List(headerSize + body.length);
final view = ByteData.view(frame.buffer);
@@ -66,6 +73,12 @@ abstract class MaxWebFraming {
final compressionRatio = view.getUint8(6);
final length =
(view.getUint8(7) << 16) | (view.getUint8(8) << 8) | view.getUint8(9);
final expectedLength = headerSize + length;
if (frame.length != expectedLength) {
throw FormatException(
'MaxWebFraming: длина кадра ${frame.length} не совпадает с $expectedLength',
);
}
if (length <= 0) {
return MaxWebFrame(cmd: cmd, seq: seq, opcode: opcode);
@@ -73,7 +86,7 @@ abstract class MaxWebFraming {
var body = Uint8List.sublistView(frame, headerSize, headerSize + length);
if (compressionRatio > 0) {
body = Lz4Block.decompress(body, length * compressionRatio * 16);
body = Lz4Block.decompress(body, maxDecompressedPayloadSize);
}
return MaxWebFrame(
@@ -87,51 +100,67 @@ abstract class MaxWebFraming {
abstract class Lz4Block {
static Uint8List decompress(Uint8List source, int maxOutputSize) {
final output = Uint8List(maxOutputSize);
if (maxOutputSize < 0) {
throw ArgumentError.value(maxOutputSize, 'maxOutputSize');
}
final output = <int>[];
var input = 0;
var written = 0;
while (input < source.length) {
final token = source[input++];
var literalLength = token >> 4;
if (literalLength == 15) {
literalLength += _readLengthExtension(source, () => input, (v) => input = v);
literalLength += _readLengthExtension(
source,
() => input,
(v) => input = v,
);
}
if (written + literalLength > output.length) {
if (literalLength > source.length - input) {
throw const FormatException('Lz4Block: обрыв литералов');
}
if (literalLength > maxOutputSize - output.length) {
throw const FormatException('Lz4Block: литералы не помещаются');
}
output.setRange(written, written + literalLength,
Uint8List.sublistView(source, input, input + literalLength));
written += literalLength;
output.addAll(
Uint8List.sublistView(source, input, input + literalLength),
);
input += literalLength;
if (input >= source.length) break;
if (source.length - input < 2) {
throw const FormatException('Lz4Block: обрыв смещения совпадения');
}
final offset = source[input] | (source[input + 1] << 8);
input += 2;
if (offset == 0 || offset > written) {
if (offset == 0 || offset > output.length) {
throw const FormatException('Lz4Block: неверное смещение совпадения');
}
var matchLength = token & 0x0F;
if (matchLength == 15) {
matchLength += _readLengthExtension(source, () => input, (v) => input = v);
matchLength += _readLengthExtension(
source,
() => input,
(v) => input = v,
);
}
matchLength += 4;
if (written + matchLength > output.length) {
if (matchLength > maxOutputSize - output.length) {
throw const FormatException('Lz4Block: совпадение не помещается');
}
var from = written - offset;
for (var i = 0; i < matchLength; i++) {
output[written++] = output[from++];
output.add(output[output.length - offset]);
}
}
return Uint8List.sublistView(output, 0, written);
return Uint8List.fromList(output);
}
static int _readLengthExtension(
@@ -191,7 +220,9 @@ abstract class MaxMsgpack {
_write(sink, item);
});
} else {
throw ArgumentError('MaxMsgpack: неподдерживаемый тип ${value.runtimeType}');
throw ArgumentError(
'MaxMsgpack: неподдерживаемый тип ${value.runtimeType}',
);
}
}
@@ -306,11 +337,8 @@ abstract class MaxMsgpack {
}
class _Reader {
_Reader(this._bytes) : _view = ByteData.view(
_bytes.buffer,
_bytes.offsetInBytes,
_bytes.length,
);
_Reader(this._bytes)
: _view = ByteData.view(_bytes.buffer, _bytes.offsetInBytes, _bytes.length);
final Uint8List _bytes;
final ByteData _view;
@@ -393,7 +421,9 @@ class _Reader {
if (byte == 0xC8) return _ext(_u16());
if (byte == 0xC9) return _ext(_u32());
throw FormatException('MaxMsgpack: неизвестный маркер 0x${byte.toRadixString(16)}');
throw FormatException(
'MaxMsgpack: неизвестный маркер 0x${byte.toRadixString(16)}',
);
}
static const int _numberExtType = 1;
+5 -2
View File
@@ -81,7 +81,8 @@ class MaxWebSocketSession {
_subscription = socket.listen(
_onFrame,
onError: (Object error) => _failAll(MaxWebException('сокет: $error')),
onDone: () => _failAll(const MaxWebException('соединение закрыто сервером')),
onDone: () =>
_failAll(const MaxWebException('соединение закрыто сервером')),
cancelOnError: true,
);
@@ -168,7 +169,9 @@ class MaxWebSocketSession {
return MaxWebException(code, code: code);
}
}
return MaxWebException('опкод ${frame.opcode}: ошибка сервера (cmd=${frame.cmd})');
return MaxWebException(
'опкод ${frame.opcode}: ошибка сервера (cmd=${frame.cmd})',
);
}
void _failAll(MaxWebException error) {
+7 -2
View File
@@ -194,7 +194,10 @@ class WebPushService {
throw const MaxWebException('время подтверждения истекло');
}
Future<WebPushAuthStep> submitPassword(String trackId, String password) async {
Future<WebPushAuthStep> submitPassword(
String trackId,
String password,
) async {
final socket = _requireSocket();
final payload = _asMap(
await socket.request(Opcode.authLoginCheckPassword, <String, Object?>{
@@ -222,7 +225,9 @@ class WebPushService {
Future<void> registerSubscription(WebPushSubscription subscription) async {
final token = await TokenStorage.readSecure(_tokenKey);
if (token == null || token.isEmpty) {
throw const MaxWebException('сначала подключите уведомления в настройках');
throw const MaxWebException(
'сначала подключите уведомления в настройках',
);
}
final socket = MaxWebSocketSession(device: await device());
+2 -2
View File
@@ -17,7 +17,7 @@ Future<File> _tempFile(String extension) async {
return File(
p.join(
dir.path,
'komet_probe_${DateTime.now().microsecondsSinceEpoch}.$extension',
'qlyra_probe_${DateTime.now().microsecondsSinceEpoch}.$extension',
),
);
}
@@ -37,7 +37,7 @@ Future<File> buildProbeZipFile({
List<int>? prefix,
}) async {
final archive = Archive();
final data = utf8.encode('This is a zip, not a photo. Komet probe.');
final data = utf8.encode('This is a zip, not a photo. Qlyra probe.');
archive.addFile(ArchiveFile('not_a_photo.txt', data.length, data));
final zip = ZipEncoder().encodeBytes(archive);
final bytes = prefix == null ? zip : <int>[...prefix, ...zip];
+1 -1
View File
@@ -24,7 +24,7 @@ Future<void> exportDebugLog(BuildContext context) async {
archive.addFile(ArchiveFile(file.name, data.length, data));
}
final bytes = ZipEncoder().encodeBytes(archive);
final fileName = 'komet_debug_${formatFileStamp(DateTime.now())}.zip';
final fileName = 'qlyra_debug_${formatFileStamp(DateTime.now())}.zip';
final isMobile = Platform.isAndroid || Platform.isIOS;
try {
final path = await FilePicker.platform.saveFile(
+1 -1
View File
@@ -7,7 +7,7 @@ import '../widgets/connection_status.dart';
import 'debug_toggle_tile.dart';
class DebugNetworkSection extends StatelessWidget {
final KometAppState? appState;
final QlyraAppState? appState;
const DebugNetworkSection({super.key, required this.appState});
@@ -1,6 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'password_2fa_screen.dart';
+6 -6
View File
@@ -3,9 +3,9 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/config/countries.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:komet/l10n/terms_of_service.dart';
import 'package:qlyra/core/config/countries.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import 'package:qlyra/l10n/terms_of_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'code_confirmation_screen.dart';
import 'token_login_screen.dart';
@@ -207,7 +207,7 @@ class _LoginScreenState extends State<LoginScreen> {
),
onTap: () {
Navigator.pop(sheetContext);
KometApp.stateOf(
QlyraApp.stateOf(
appContext,
)?.applyLocale(const Locale('ru'));
},
@@ -223,7 +223,7 @@ class _LoginScreenState extends State<LoginScreen> {
),
onTap: () {
Navigator.pop(sheetContext);
KometApp.stateOf(
QlyraApp.stateOf(
appContext,
)?.applyLocale(const Locale('en'));
},
@@ -791,7 +791,7 @@ class _LoginScreenState extends State<LoginScreen> {
behavior: HitTestBehavior.opaque,
onTap: _onLogoTap,
child: Image.asset(
'assets/komet.png',
'assets/qlyra.png',
height: 80,
color: cs.onSurface,
),
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:komet/backend/api.dart';
import 'package:komet/core/config/proxy_config.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/backend/api.dart';
import 'package:qlyra/core/config/proxy_config.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import '../../../main.dart';
import '../../widgets/custom_notification.dart';
@@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../../backend/modules/account.dart';
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/config/countries.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/core/config/countries.dart';
import 'package:qlyra/l10n/app_localizations.dart';
class SelectCountryScreen extends StatefulWidget {
final CountryName selectedCountry;
@@ -2,9 +2,9 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:komet/backend/api.dart';
import 'package:komet/core/config/config.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/backend/api.dart';
import 'package:qlyra/core/config/config.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../main.dart';
@@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/backend/modules/calls.dart';
import 'package:komet/frontend/screens/chats/chat_list_screen.dart';
import 'package:komet/frontend/screens/contacts/contact_sheet_common.dart';
import 'package:komet/frontend/widgets/custom_notification.dart';
import 'package:komet/frontend/widgets/small_spinner.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:komet/main.dart' show messagesModule;
import 'package:qlyra/backend/modules/calls.dart';
import 'package:qlyra/frontend/screens/chats/chat_list_screen.dart';
import 'package:qlyra/frontend/screens/contacts/contact_sheet_common.dart';
import 'package:qlyra/frontend/widgets/custom_notification.dart';
import 'package:qlyra/frontend/widgets/small_spinner.dart';
import 'package:qlyra/l10n/app_localizations.dart';
import 'package:qlyra/main.dart' show messagesModule;
import '../../../core/config/app_shape.dart';
Future<bool> showCreatedCallSheet(
@@ -7,7 +7,7 @@ import '../../../core/calls/call_admin.dart';
import '../../../core/calls/call_session.dart';
import '../../widgets/animated_slash_icon.dart';
import '../../widgets/custom_notification.dart';
import '../../widgets/komet_avatar.dart';
import '../../widgets/qlyra_avatar.dart';
import '../../widgets/prompt_dialog.dart';
import '../../widgets/sheet_helpers.dart';
import '../../../core/config/app_fonts.dart';
@@ -479,7 +479,7 @@ class _ParticipantsSheetState extends State<_ParticipantsSheet> {
];
return ListTile(
leading: KometAvatar(name: view.name, imageUrl: view.avatarUrl, size: 40),
leading: QlyraAvatar(name: view.name, imageUrl: view.avatarUrl, size: 40),
title: Text(
view.name,
style: TextStyle(color: cs.onSurface, fontSize: 16),
+13 -13
View File
@@ -27,7 +27,7 @@ import '../../widgets/sheet_helpers.dart';
import '../../widgets/small_spinner.dart';
import 'call_mic_sheet.dart';
import 'call_participants_sheet.dart';
import 'komet_hub.dart';
import 'qlyra_hub.dart';
import '../../../core/config/app_fonts.dart';
class CallScreen extends StatefulWidget {
@@ -57,7 +57,7 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
StreamSubscription<CallSessionState>? _stateSub;
StreamSubscription<void>? _canceledSub;
StreamSubscription<void>? _infoSub;
StreamSubscription<void>? _kometSub;
StreamSubscription<void>? _qlyraSub;
StreamSubscription<CallChatMessage>? _chatSub;
StreamSubscription<MediaStream>? _remoteStreamSub;
bool _chatOpen = false;
@@ -252,10 +252,10 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
});
_remoteStreamSub = session.remoteStreamStream.listen(_attachStream);
_tileStreamSub = session.participantStreamUpdates.listen(_onTileStream);
_kometSub = session.peerKometDetected.listen((_) => _showKometBadge());
_qlyraSub = session.peerQlyraDetected.listen((_) => _showQlyraBadge());
_chatSub = session.chatMessages.listen(_onChatMessage);
if (session.peerIsKomet) {
WidgetsBinding.instance.addPostFrameCallback((_) => _showKometBadge());
if (session.peerIsQlyra) {
WidgetsBinding.instance.addPostFrameCallback((_) => _showQlyraBadge());
}
final existing = session.remoteStream;
if (existing != null) _attachStream(existing);
@@ -276,10 +276,10 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
);
}
void _showKometBadge() {
void _showQlyraBadge() {
if (!mounted) return;
final l10n = AppLocalizations.of(context)!;
showCustomNotification(context, l10n.callKometDetectedNotification);
showCustomNotification(context, l10n.callQlyraDetectedNotification);
}
void _onChatMessage(CallChatMessage message) {
@@ -287,11 +287,11 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
showCustomNotification(context, message.text);
}
Future<void> _openKometHub() async {
Future<void> _openQlyraHub() async {
final session = _session;
if (session == null) return;
setState(() => _chatOpen = true);
await showKometHub(context, session: session, scheme: _darkScheme(context));
await showQlyraHub(context, session: session, scheme: _darkScheme(context));
if (mounted) setState(() => _chatOpen = false);
}
@@ -428,7 +428,7 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
_stateSub?.cancel();
_canceledSub?.cancel();
_infoSub?.cancel();
_kometSub?.cancel();
_qlyraSub?.cancel();
_chatSub?.cancel();
_remoteStreamSub?.cancel();
_tileStreamSub?.cancel();
@@ -1010,10 +1010,10 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (_session?.peerIsKomet == true)
if (_session?.peerIsQlyra == true)
IconButton(
onPressed: _openKometHub,
tooltip: l10n.callTooltipKometHub,
onPressed: _openQlyraHub,
tooltip: l10n.callTooltipQlyraHub,
icon: Icon(
Symbols.auto_awesome,
color: cs.primary,
+2 -2
View File
@@ -8,7 +8,7 @@ import '../../../core/storage/app_database.dart';
import '../../../core/utils/format.dart';
import '../../../core/calls/call_controller.dart';
import '../../../backend/modules/calls.dart';
import '../../widgets/komet_avatar.dart';
import '../../widgets/qlyra_avatar.dart';
import '../../widgets/connection_status.dart';
import '../../widgets/reload_on_reconnect.dart';
import '../../widgets/custom_notification.dart';
@@ -183,7 +183,7 @@ class _CallsTabState extends State<CallsTab>
color: cs.onPrimaryContainer,
size: 26,
)
: KometAvatar(
: QlyraAvatar(
name: call.name,
imageUrl: call.avatarUrl,
size: 48,
@@ -9,7 +9,7 @@ import '../../../l10n/app_localizations.dart';
import '../../widgets/sheet_helpers.dart';
import '../../../core/config/app_fonts.dart';
Future<void> showKometHub(
Future<void> showQlyraHub(
BuildContext context, {
required CallSession session,
required ColorScheme scheme,
@@ -22,23 +22,23 @@ Future<void> showKometHub(
shape: kSheetShape,
builder: (_) => Theme(
data: Theme.of(context).copyWith(colorScheme: scheme),
child: _KometHub(session: session),
child: _QlyraHub(session: session),
),
);
}
enum _HubPage { menu, chat, games, checkers }
class _KometHub extends StatefulWidget {
class _QlyraHub extends StatefulWidget {
final CallSession session;
const _KometHub({required this.session});
const _QlyraHub({required this.session});
@override
State<_KometHub> createState() => _KometHubState();
State<_QlyraHub> createState() => _QlyraHubState();
}
class _KometHubState extends State<_KometHub> {
class _QlyraHubState extends State<_QlyraHub> {
_HubPage _page = _HubPage.menu;
void _go(_HubPage page) => setState(() => _page = page);
@@ -131,7 +131,7 @@ class _KometHubState extends State<_KometHub> {
case _HubPage.games:
return _games(cs);
case _HubPage.chat:
return _KometChatView(session: widget.session);
return _QlyraChatView(session: widget.session);
case _HubPage.checkers:
return _CheckersView(session: widget.session);
}
@@ -224,16 +224,16 @@ class _KometHubState extends State<_KometHub> {
}
}
class _KometChatView extends StatefulWidget {
class _QlyraChatView extends StatefulWidget {
final CallSession session;
const _KometChatView({required this.session});
const _QlyraChatView({required this.session});
@override
State<_KometChatView> createState() => _KometChatViewState();
State<_QlyraChatView> createState() => _QlyraChatViewState();
}
class _KometChatViewState extends State<_KometChatView> {
class _QlyraChatViewState extends State<_QlyraChatView> {
final TextEditingController _controller = TextEditingController();
final ScrollController _scroll = ScrollController();
StreamSubscription<CallChatMessage>? _sub;
@@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
import '../../../../backend/modules/chats.dart';
import '../../../../backend/modules/messages.dart';
import '../../../../core/cache/message_session_cache.dart';
import '../../../../core/config/komet_settings.dart';
import '../../../../core/config/qlyra_settings.dart';
import '../../../../core/storage/app_database.dart';
import '../../../../core/utils/logger.dart';
import '../../../../main.dart';
@@ -168,7 +168,7 @@ class ChatController extends ChangeNotifier {
required int targetTime,
}) async {
if (myId == 0 || targetTime <= 0) return false;
final onlyVisible = !KometSettings.viewDeleted.value;
final onlyVisible = !QlyraSettings.viewDeleted.value;
var window = await loadWindowFromDb(targetTime, onlyVisible);
if (!isMounted()) return false;
@@ -182,7 +182,7 @@ class ChatController extends ChangeNotifier {
backward: jumpWindowBefore + 1,
);
if (!isMounted()) return false;
if (fetched.isNotEmpty && KometSettings.viewDeleted.value) {
if (fetched.isNotEmpty && QlyraSettings.viewDeleted.value) {
await chats.reconcileDeletedFromFetch(myId, chatId, fetched);
}
window = await loadWindowFromDb(targetTime, onlyVisible);
@@ -236,7 +236,7 @@ class ChatController extends ChangeNotifier {
loadingGap = true;
try {
final onlyVisible = !KometSettings.viewDeleted.value;
final onlyVisible = !QlyraSettings.viewDeleted.value;
var slice = await loadGapSliceFromDb(
gap.edgeTime,
gap.tailTime,
@@ -253,7 +253,7 @@ class ChatController extends ChangeNotifier {
backward: 0,
);
if (!isMounted()) return 0;
if (fetched.isNotEmpty && KometSettings.viewDeleted.value) {
if (fetched.isNotEmpty && QlyraSettings.viewDeleted.value) {
await chats.reconcileDeletedFromFetch(myId, chatId, fetched);
}
final refreshed = await loadGapSliceFromDb(
@@ -320,7 +320,7 @@ class ChatController extends ChangeNotifier {
final size = pageSize ?? historyPageSize;
final oldest = messages.first;
final onlyVisible = !KometSettings.viewDeleted.value;
final onlyVisible = !QlyraSettings.viewDeleted.value;
try {
var older = await loadOlderFromDb(oldest.time, onlyVisible, limit: size);
@@ -333,7 +333,7 @@ class ChatController extends ChangeNotifier {
count: size,
);
if (fetched.isNotEmpty) {
if (KometSettings.viewDeleted.value) {
if (QlyraSettings.viewDeleted.value) {
await chats.reconcileDeletedFromFetch(myId, chatId, fetched);
}
older = await loadOlderFromDb(oldest.time, onlyVisible, limit: size);
@@ -359,7 +359,7 @@ class ChatController extends ChangeNotifier {
required void Function() onPreview,
required void Function() onSenderNames,
}) async {
final onlyVisible = !KometSettings.viewDeleted.value;
final onlyVisible = !QlyraSettings.viewDeleted.value;
final cachedRows = await AppDatabase.loadChat(myId, chatId);
final preview =
cachedRows.isEmpty || !AppDatabase.chatRowIsInList(cachedRows.first);
@@ -387,7 +387,7 @@ class ChatController extends ChangeNotifier {
try {
final serverMessages = await messagesModule.fetchHistory(myId, chatId);
chats.markHistoryFetched(chatId);
if (KometSettings.viewDeleted.value) {
if (QlyraSettings.viewDeleted.value) {
await chats.reconcileDeletedFromFetch(myId, chatId, serverMessages);
}
final updatedDecoded = await loadInitialFromDb(onlyVisible: onlyVisible);
@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/widgets.dart';
import '../../../../core/config/komet_settings.dart';
import '../../../../core/config/qlyra_settings.dart';
class StickerPanelController {
StickerPanelController({
@@ -66,7 +66,7 @@ class StickerPanelController {
}
void _sendTyping() {
if (KometSettings.ghostMode.value) return;
if (QlyraSettings.ghostMode.value) return;
onSendTyping();
}
@@ -4,18 +4,18 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/config/app_frost.dart';
import 'package:komet/core/config/app_stories.dart';
import 'package:komet/core/utils/haptics.dart';
import 'package:komet/frontend/screens/stories/story_owner_info.dart';
import 'package:komet/frontend/screens/stories/story_ring.dart';
import 'package:komet/frontend/screens/stories/story_viewer_screen.dart';
import 'package:komet/frontend/widgets/encryption_lock_badge.dart';
import 'package:komet/frontend/widgets/glossy_pill.dart';
import 'package:komet/frontend/widgets/online_dot.dart';
import 'package:komet/frontend/widgets/profile_hero.dart';
import 'package:komet/main.dart' show storiesModule;
import 'package:komet/models/story.dart';
import 'package:qlyra/core/config/app_frost.dart';
import 'package:qlyra/core/config/app_stories.dart';
import 'package:qlyra/core/utils/haptics.dart';
import 'package:qlyra/frontend/screens/stories/story_owner_info.dart';
import 'package:qlyra/frontend/screens/stories/story_ring.dart';
import 'package:qlyra/frontend/screens/stories/story_viewer_screen.dart';
import 'package:qlyra/frontend/widgets/encryption_lock_badge.dart';
import 'package:qlyra/frontend/widgets/glossy_pill.dart';
import 'package:qlyra/frontend/widgets/online_dot.dart';
import 'package:qlyra/frontend/widgets/profile_hero.dart';
import 'package:qlyra/main.dart' show storiesModule;
import 'package:qlyra/models/story.dart';
import '../../../../../core/config/app_fonts.dart';
class ChatHeaderRow extends StatelessWidget {
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:komet/core/storage/chat_activity_store.dart';
import 'package:komet/frontend/screens/chats/chat/typing_label.dart';
import 'package:komet/frontend/widgets/animated_text_swap.dart';
import 'package:qlyra/core/storage/chat_activity_store.dart';
import 'package:qlyra/frontend/screens/chats/chat/typing_label.dart';
import 'package:qlyra/frontend/widgets/animated_text_swap.dart';
class AnimatedChatTile extends StatefulWidget {
final Widget child;
@@ -4,9 +4,9 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/utils/text_format.dart';
import 'package:komet/frontend/widgets/formatted_message_text.dart';
import 'package:komet/models/chat_preview_media.dart';
import 'package:qlyra/core/utils/text_format.dart';
import 'package:qlyra/frontend/widgets/formatted_message_text.dart';
import 'package:qlyra/models/chat_preview_media.dart';
const String _forwardMark = '';
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:komet/frontend/commands/slash_command.dart';
import 'package:komet/frontend/screens/chats/chat/command_panel_controller.dart';
import 'package:komet/frontend/widgets/command_suggestions_panel.dart';
import 'package:qlyra/frontend/commands/slash_command.dart';
import 'package:qlyra/frontend/screens/chats/chat/command_panel_controller.dart';
import 'package:qlyra/frontend/widgets/command_suggestions_panel.dart';
class CommandPanelView extends StatelessWidget {
const CommandPanelView({super.key, required this.commandPanel});
@@ -5,19 +5,19 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/backend/modules/messages.dart';
import 'package:komet/core/config/app_chat_chrome.dart';
import 'package:komet/core/config/app_colors.dart';
import 'package:komet/core/config/app_composer_background.dart';
import 'package:komet/core/config/app_composer_style.dart';
import 'package:komet/core/config/app_frost.dart';
import 'package:komet/frontend/screens/chats/chat/upload_status.dart';
import 'package:komet/frontend/screens/chats/chat/video_note_controller.dart';
import 'package:komet/frontend/screens/chats/chat/voice_record_controller.dart';
import 'package:komet/frontend/widgets/composer_morph_icon.dart';
import 'package:komet/frontend/widgets/glossy_pill.dart';
import 'package:komet/frontend/widgets/liquid_glass.dart';
import 'package:komet/frontend/widgets/rich_message_controller.dart';
import 'package:qlyra/backend/modules/messages.dart';
import 'package:qlyra/core/config/app_chat_chrome.dart';
import 'package:qlyra/core/config/app_colors.dart';
import 'package:qlyra/core/config/app_composer_background.dart';
import 'package:qlyra/core/config/app_composer_style.dart';
import 'package:qlyra/core/config/app_frost.dart';
import 'package:qlyra/frontend/screens/chats/chat/upload_status.dart';
import 'package:qlyra/frontend/screens/chats/chat/video_note_controller.dart';
import 'package:qlyra/frontend/screens/chats/chat/voice_record_controller.dart';
import 'package:qlyra/frontend/widgets/composer_morph_icon.dart';
import 'package:qlyra/frontend/widgets/glossy_pill.dart';
import 'package:qlyra/frontend/widgets/liquid_glass.dart';
import 'package:qlyra/frontend/widgets/rich_message_controller.dart';
class ComposerInputBar extends StatelessWidget {
const ComposerInputBar({
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:komet/frontend/screens/chats/chat/mention_panel_controller.dart';
import 'package:komet/frontend/widgets/mention_suggestions_panel.dart';
import 'package:qlyra/frontend/screens/chats/chat/mention_panel_controller.dart';
import 'package:qlyra/frontend/widgets/mention_suggestions_panel.dart';
class MentionPanelView extends StatelessWidget {
const MentionPanelView({super.key, required this.mentionPanel});
@@ -3,15 +3,15 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/config/app_animations.dart';
import 'package:komet/core/config/app_chat_chrome.dart';
import 'package:komet/core/utils/format.dart';
import 'package:komet/frontend/widgets/animated_lottie_icon.dart';
import 'package:komet/frontend/widgets/glossy_pill.dart';
import 'package:komet/frontend/widgets/komet_avatar.dart';
import 'package:komet/frontend/widgets/small_spinner.dart';
import 'package:komet/frontend/screens/chats/chat/chat_search_controller.dart';
import 'package:komet/frontend/screens/chats/chat/message_search_result.dart';
import 'package:qlyra/core/config/app_animations.dart';
import 'package:qlyra/core/config/app_chat_chrome.dart';
import 'package:qlyra/core/utils/format.dart';
import 'package:qlyra/frontend/widgets/animated_lottie_icon.dart';
import 'package:qlyra/frontend/widgets/glossy_pill.dart';
import 'package:qlyra/frontend/widgets/qlyra_avatar.dart';
import 'package:qlyra/frontend/widgets/small_spinner.dart';
import 'package:qlyra/frontend/screens/chats/chat/chat_search_controller.dart';
import 'package:qlyra/frontend/screens/chats/chat/message_search_result.dart';
import '../../../../../core/config/app_fonts.dart';
class SearchTopBar extends StatelessWidget {
@@ -212,7 +212,7 @@ class SearchOverlay extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
KometAvatar(
QlyraAvatar(
name: name,
imageUrl: senderAvatar(r.senderId),
size: 44,
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/backend/modules/messages.dart';
import 'package:komet/frontend/widgets/glossy_pill.dart';
import 'package:qlyra/backend/modules/messages.dart';
import 'package:qlyra/frontend/widgets/glossy_pill.dart';
import '../../../../../core/config/app_fonts.dart';
class SelectionTopBar extends StatelessWidget {
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:komet/frontend/screens/chats/chat/sticker_panel_controller.dart';
import 'package:komet/frontend/widgets/lottie_image.dart';
import 'package:komet/frontend/widgets/sticker_panel.dart';
import 'package:komet/models/animoji.dart';
import 'package:komet/models/sticker.dart';
import 'package:qlyra/frontend/screens/chats/chat/sticker_panel_controller.dart';
import 'package:qlyra/frontend/widgets/lottie_image.dart';
import 'package:qlyra/frontend/widgets/sticker_panel.dart';
import 'package:qlyra/models/animoji.dart';
import 'package:qlyra/models/sticker.dart';
class StickerPanelView extends StatelessWidget {
const StickerPanelView({
@@ -8,7 +8,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart' show listEquals;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:komet/main.dart';
import 'package:qlyra/main.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../contacts/edit_contact_sheet.dart';
import '../../../backend/modules/complaints.dart';
@@ -37,7 +37,7 @@ import '../../widgets/custom_notification.dart';
import '../../widgets/formatted_message_text.dart';
import '../../widgets/reload_on_reconnect.dart';
import '../../widgets/glossy_pill.dart';
import '../../widgets/komet_avatar.dart';
import '../../widgets/qlyra_avatar.dart';
import '../../widgets/profile_header_scroll.dart';
import '../../widgets/profile_hero.dart';
import '../../widgets/swipe_route.dart';
@@ -1079,7 +1079,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen>
}
final url = _avatarPages.isNotEmpty ? _avatarPages.first : widget.imageUrl;
if (url.isEmpty) {
return KometAvatar(
return QlyraAvatar(
name: widget.name,
size: _headerAvatarSize,
fontSize: 36,
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:komet/backend/modules/messages.dart';
import 'package:qlyra/backend/modules/messages.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'dart:math';
import 'dart:ui' as ui;
@@ -61,7 +61,7 @@ import '../../../core/cache/info_cache.dart';
import '../../../core/config/app_visual_style.dart';
import '../../../core/config/app_stories.dart';
import '../../../core/config/app_colors.dart';
import '../../../core/config/komet_settings.dart';
import '../../../core/config/qlyra_settings.dart';
import '../../../backend/models/chat_folder.dart';
import '../../../backend/modules/account.dart';
import '../../../backend/modules/chats.dart';
@@ -769,8 +769,8 @@ class _ChatListScreenState extends State<ChatListScreen>
DraftStore.instance.revision.addListener(_onDraftsChanged);
AppStories.current.addListener(_onStoriesEnabledChanged);
storiesModule.storiesChanged.addListener(_onStoriesDataChanged);
KometSettings.hideAllChatsFolder.addListener(_requestReload);
KometSettings.showHiddenChats.addListener(_requestReload);
QlyraSettings.hideAllChatsFolder.addListener(_requestReload);
QlyraSettings.showHiddenChats.addListener(_requestReload);
ContactsModule.revision.addListener(_requestReload);
FoldersModule.revision.addListener(_requestReload);
bannersModule.activeBanner.addListener(_onActiveInformerChanged);
@@ -964,7 +964,7 @@ class _ChatListScreenState extends State<ChatListScreen>
final loadedChats = await chats.getChats(
p.id,
includeHidden:
widget.archiveMode || KometSettings.showHiddenChats.value,
widget.archiveMode || QlyraSettings.showHiddenChats.value,
);
final archivedIds = ArchivedChatsStore.instance.archivedChatIds(p.id);
var archivedCount = 0;
@@ -993,7 +993,7 @@ class _ChatListScreenState extends State<ChatListScreen>
final hasRealFolders = folders.any(
(f) => !FoldersModule.isAllChatsFolder(f),
);
if (KometSettings.hideAllChatsFolder.value && hasRealFolders) {
if (QlyraSettings.hideAllChatsFolder.value && hasRealFolders) {
folders = folders
.where((f) => !FoldersModule.isAllChatsFolder(f))
.toList();
@@ -1474,8 +1474,8 @@ class _ChatListScreenState extends State<ChatListScreen>
DraftStore.instance.revision.removeListener(_onDraftsChanged);
AppStories.current.removeListener(_onStoriesEnabledChanged);
storiesModule.storiesChanged.removeListener(_onStoriesDataChanged);
KometSettings.hideAllChatsFolder.removeListener(_requestReload);
KometSettings.showHiddenChats.removeListener(_requestReload);
QlyraSettings.hideAllChatsFolder.removeListener(_requestReload);
QlyraSettings.showHiddenChats.removeListener(_requestReload);
ContactsModule.revision.removeListener(_requestReload);
FoldersModule.revision.removeListener(_requestReload);
bannersModule.activeBanner.removeListener(_onActiveInformerChanged);
@@ -2028,18 +2028,24 @@ class _ChatListScreenState extends State<ChatListScreen>
if (chat.type.isNotEmpty &&
chat.type == "DIALOG" &&
chat.id != 0) {
int secondId = _profile?.id ?? 0;
int? secondId;
for (final entry in chat.participants.entries) {
if (entry.key != _profile?.id) {
secondId = entry.key;
break;
}
}
final name = ContactCache.get(secondId) ?? chat.title;
final avatar =
ContactCache.getAvatar(secondId) ?? chat.iconUrl;
final name = secondId == null
? chat.title
: ContactCache.get(secondId) ?? chat.title;
final avatar = secondId == null
? chat.iconUrl
: ContactCache.getAvatar(secondId) ?? chat.iconUrl;
final isVerified =
ContactCache.isOfficial(secondId) || chat.isOfficial;
(secondId != null &&
ContactCache.isOfficial(secondId)) ||
chat.isOfficial;
final presenceUserId = secondId ?? 0;
final isPlaceholder = chat.isLastMsgDeleted;
final previewText = isPlaceholder
@@ -2053,7 +2059,7 @@ class _ChatListScreenState extends State<ChatListScreen>
previewText,
_formatTime(chat.lastMsgTime),
avatar ?? "",
presenceUserId: secondId,
presenceUserId: presenceUserId,
unreadCount: chat.unreadCount,
hasMention: chat.hasUnreadMention,
isMuted: chat.isMuted,
@@ -2074,9 +2080,9 @@ class _ChatListScreenState extends State<ChatListScreen>
previewMedia: isPlaceholder ? null : chat.lastMsgMedia,
titleIcon: chatKindIcon(
'DIALOG',
isBot: _isBotDialog(secondId, chat),
isBot: _isBotDialog(presenceUserId, chat),
),
hasMiniApp: _hasMiniApp(secondId, chat),
hasMiniApp: _hasMiniApp(presenceUserId, chat),
),
);
} else {
@@ -3614,7 +3620,7 @@ class _ChatListScreenState extends State<ChatListScreen>
if (p == null) return;
final all = await chats.getChats(
p.id,
includeHidden: KometSettings.showHiddenChats.value,
includeHidden: QlyraSettings.showHiddenChats.value,
);
final targets = all
.where((c) => c.unreadCount > 0)
+26 -26
View File
@@ -9,24 +9,24 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:komet/backend/modules/chat_preview.dart';
import 'package:komet/backend/modules/chats.dart';
import 'package:komet/backend/modules/comments.dart';
import 'package:komet/backend/modules/upload_service.dart';
import 'package:komet/backend/modules/webapp.dart';
import 'package:komet/frontend/screens/webapp/open_mini_app.dart';
import 'package:komet/frontend/widgets/sending_clock_icon.dart';
import 'package:komet/core/media/desktop_video_probe.dart';
import 'package:komet/core/media/video_transcoder.dart';
import 'package:komet/core/media/gallery_source.dart';
import 'package:komet/core/utils/format.dart';
import 'package:komet/frontend/screens/chats/chat_info_screen.dart';
import 'package:komet/frontend/screens/contacts/open_contact_profile.dart';
import 'package:komet/frontend/screens/chats/chat_list_screen.dart';
import 'package:komet/frontend/screens/chats/poll_create_screen.dart';
import 'package:komet/frontend/widgets/animated_text_swap.dart';
import 'package:komet/frontend/widgets/custom_notification.dart';
import 'package:komet/frontend/widgets/chat_menu_overlay.dart';
import 'package:qlyra/backend/modules/chat_preview.dart';
import 'package:qlyra/backend/modules/chats.dart';
import 'package:qlyra/backend/modules/comments.dart';
import 'package:qlyra/backend/modules/upload_service.dart';
import 'package:qlyra/backend/modules/webapp.dart';
import 'package:qlyra/frontend/screens/webapp/open_mini_app.dart';
import 'package:qlyra/frontend/widgets/sending_clock_icon.dart';
import 'package:qlyra/core/media/desktop_video_probe.dart';
import 'package:qlyra/core/media/video_transcoder.dart';
import 'package:qlyra/core/media/gallery_source.dart';
import 'package:qlyra/core/utils/format.dart';
import 'package:qlyra/frontend/screens/chats/chat_info_screen.dart';
import 'package:qlyra/frontend/screens/contacts/open_contact_profile.dart';
import 'package:qlyra/frontend/screens/chats/chat_list_screen.dart';
import 'package:qlyra/frontend/screens/chats/poll_create_screen.dart';
import 'package:qlyra/frontend/widgets/animated_text_swap.dart';
import 'package:qlyra/frontend/widgets/custom_notification.dart';
import 'package:qlyra/frontend/widgets/chat_menu_overlay.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../../main.dart';
import '../../../l10n/app_localizations.dart';
@@ -85,10 +85,10 @@ import 'chat/view/shimmer_loading.dart';
import '../../../core/config/app_commands.dart';
import '../../../core/config/app_visual_style.dart';
import '../../../core/config/app_chat_chrome.dart';
import 'package:komet/core/config/app_composer_background.dart';
import 'package:komet/core/config/app_frost.dart';
import 'package:komet/core/config/app_composer_style.dart';
import '../../../core/config/komet_settings.dart';
import 'package:qlyra/core/config/app_composer_background.dart';
import 'package:qlyra/core/config/app_frost.dart';
import 'package:qlyra/core/config/app_composer_style.dart';
import '../../../core/config/qlyra_settings.dart';
import '../../../models/attachment.dart';
import '../../../models/contact_info.dart';
import '../../../models/sticker.dart';
@@ -938,7 +938,7 @@ class _ChatScreenState extends State<ChatScreen>
_myId,
widget.chatId,
limit: 20,
onlyVisible: !KometSettings.viewDeleted.value,
onlyVisible: !QlyraSettings.viewDeleted.value,
);
if (!mounted) return;
if (firstRows.isNotEmpty) {
@@ -2872,7 +2872,7 @@ class _ChatScreenState extends State<ChatScreen>
final idx = _messages.indexWhere((m) => m.id == message.id);
if (idx != -1) {
final old = _messages[idx];
final newHistory = KometSettings.viewRedacted.value
final newHistory = QlyraSettings.viewRedacted.value
? CachedMessage.appendEditHistory(
old.editHistory,
old.text,
@@ -3640,7 +3640,7 @@ class _ChatScreenState extends State<ChatScreen>
_myId,
widget.chatId,
);
if (KometSettings.viewDeleted.value) {
if (QlyraSettings.viewDeleted.value) {
await chats.reconcileDeletedFromFetch(
_myId,
widget.chatId,
@@ -3651,7 +3651,7 @@ class _ChatScreenState extends State<ChatScreen>
_myId,
widget.chatId,
limit: 100,
onlyVisible: !KometSettings.viewDeleted.value,
onlyVisible: !QlyraSettings.viewDeleted.value,
);
final decoded = await CachedMessage.fromDbRowsAsync(rows);
if (mounted) _applyMergedMessages(decoded);
@@ -4,8 +4,8 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/core/storage/chat_wallpaper_store.dart';
import 'package:komet/frontend/widgets/chat_wallpaper_view.dart';
import 'package:qlyra/core/storage/chat_wallpaper_store.dart';
import 'package:qlyra/frontend/widgets/chat_wallpaper_view.dart';
import '../../../core/config/app_frost.dart';
import '../../../core/config/app_fonts.dart';
@@ -11,7 +11,7 @@ import '../../../core/utils/image_utils.dart';
import '../../../core/utils/names.dart';
import '../../../main.dart';
import '../../widgets/custom_notification.dart';
import '../../widgets/komet_avatar.dart';
import '../../widgets/qlyra_avatar.dart';
import '../../widgets/sheet_helpers.dart';
import '../../widgets/small_spinner.dart';
import '../../widgets/swipe_route.dart';
@@ -314,7 +314,7 @@ class _CreateGroupFlowState extends State<_CreateGroupFlow> {
),
child: Row(
children: [
KometAvatar(
QlyraAvatar(
name: c.firstName,
size: 40,
imageUrl: c.baseUrl,
@@ -527,7 +527,7 @@ class _SelectedChip extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
KometAvatar(
QlyraAvatar(
name: contact.firstName,
size: 24,
imageUrl: contact.baseUrl,
@@ -13,7 +13,7 @@ import '../../../core/utils/haptics.dart';
import '../../../main.dart';
import '../../widgets/confirm_dialog.dart';
import '../../widgets/custom_notification.dart';
import '../../widgets/komet_avatar.dart';
import '../../widgets/qlyra_avatar.dart';
import '../../widgets/sheet_helpers.dart';
import '../../widgets/small_spinner.dart';
@@ -435,7 +435,7 @@ class _FolderEditSheetState extends State<_FolderEditSheet> {
for (final chat in visibleChats)
_buildRow(
cs,
leading: KometAvatar(
leading: QlyraAvatar(
name: _chatTitle(chat),
size: 40,
imageUrl: _chatAvatar(chat),
@@ -2,16 +2,16 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:komet/main.dart';
import 'package:komet/backend/modules/chats.dart';
import 'package:komet/backend/modules/contacts.dart';
import 'package:komet/backend/modules/messages.dart' show ContactCache;
import 'package:komet/core/storage/app_database.dart';
import 'package:komet/core/storage/token_storage.dart';
import 'package:komet/frontend/screens/contacts/contact_sheet_common.dart';
import 'package:komet/frontend/widgets/custom_notification.dart';
import 'package:komet/frontend/widgets/komet_avatar.dart';
import 'package:komet/l10n/app_localizations.dart';
import 'package:qlyra/main.dart';
import 'package:qlyra/backend/modules/chats.dart';
import 'package:qlyra/backend/modules/contacts.dart';
import 'package:qlyra/backend/modules/messages.dart' show ContactCache;
import 'package:qlyra/core/storage/app_database.dart';
import 'package:qlyra/core/storage/token_storage.dart';
import 'package:qlyra/frontend/screens/contacts/contact_sheet_common.dart';
import 'package:qlyra/frontend/widgets/custom_notification.dart';
import 'package:qlyra/frontend/widgets/qlyra_avatar.dart';
import 'package:qlyra/l10n/app_localizations.dart';
class _Candidate {
final int id;
@@ -281,7 +281,7 @@ class _AddMembersCardState extends State<_AddMembersCard> {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
KometAvatar(name: c.name, imageUrl: c.avatarUrl, size: 42),
QlyraAvatar(name: c.name, imageUrl: c.avatarUrl, size: 42),
const SizedBox(width: 14),
Expanded(
child: Text(
@@ -296,9 +296,7 @@ class _AddMembersCardState extends State<_AddMembersCard> {
),
),
Icon(
selected
? Symbols.check_circle
: Symbols.radio_button_unchecked,
selected ? Symbols.check_circle : Symbols.radio_button_unchecked,
fill: selected ? 1 : 0,
color: selected ? cs.primary : cs.outline,
size: 24,
@@ -424,7 +422,7 @@ class _InviteLinkCard extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(12, 10, 6, 10),
child: Row(
children: [
KometAvatar(name: title, imageUrl: avatarUrl, size: 40),
QlyraAvatar(name: title, imageUrl: avatarUrl, size: 40),
const SizedBox(width: 12),
Expanded(
child: Column(

Some files were not shown because too many files have changed in this diff Show More