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
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:
+12
-12
@@ -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 {
|
||||
|
||||
@@ -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)');
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {},
|
||||
),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Vendored
+1
-2
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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].
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 +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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; // Запрос активных сессий
|
||||
|
||||
@@ -11,7 +11,7 @@ abstract class CmdType {
|
||||
|
||||
/// Распакованный пакет.
|
||||
///
|
||||
/// Провод (фрейминг, MsgPack, сжатие) живёт в Rust-ядре kolibri; здесь пакет —
|
||||
/// Провод (фрейминг, MsgPack, сжатие) живёт в Rust-ядре kusoft; здесь пакет —
|
||||
/// это уже декодированный [payload] (Map/List/скаляр, бинарь — Uint8List) плюс
|
||||
/// метаданные заголовка.
|
||||
class Packet {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -31,5 +31,4 @@ abstract class DeviceIdentity {
|
||||
await prefs.setString(_deviceIdKey, generated);
|
||||
return generated;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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} часа');
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>{};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+11
-11
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user