feat(links): поддержка max.ru-ссылок и komet:// диплинков
- резолв max.ru/{join,joincall,u,c,:auth,<channel>}
- открытие чатов/профилей/звонков и подтверждение веб-входа в приложении
- внешние диплинки: intent-filter (Android), URL-схемы (iOS/macOS), komet://-регистрация в рантайме (Windows/Linux)
- fix(calls): не слать opcode 79 до login (жижа я тебя выебу)
This commit is contained in:
@@ -428,11 +428,20 @@ class LoginResult {
|
||||
class AccountModule {
|
||||
final Api _api;
|
||||
final _loginStatusController = StreamController<LoginStatus>.broadcast();
|
||||
bool _loggedIn = false;
|
||||
|
||||
AccountModule(this._api);
|
||||
AccountModule(this._api) {
|
||||
_api.stateStream.listen((state) {
|
||||
if (state != SessionState.online) _loggedIn = false;
|
||||
});
|
||||
}
|
||||
|
||||
Stream<LoginStatus> get loginStatusStream => _loginStatusController.stream;
|
||||
|
||||
/// `true`, только когда сервер считает сессию ONLINE — после успешного
|
||||
/// login (opcode 19), а не просто после хэндшейка (opcode 6).
|
||||
bool get isLoggedIn => _loggedIn;
|
||||
|
||||
Future<PrivacyConfig> getPrivacyConfig() async {
|
||||
final accountId = await TokenStorage.getActiveAccountId();
|
||||
if (accountId != null) {
|
||||
@@ -999,6 +1008,7 @@ class AccountModule {
|
||||
}
|
||||
|
||||
final result = await _processLoginResponse(dataMap, resolvedAccountId);
|
||||
_loggedIn = true;
|
||||
_loginStatusController.add(LoginStatus.success);
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
import '../../core/protocol/packet.dart';
|
||||
import '../api.dart';
|
||||
|
||||
sealed class ResolvedLink {
|
||||
const ResolvedLink();
|
||||
}
|
||||
|
||||
class ResolvedChat extends ResolvedLink {
|
||||
final Map<dynamic, dynamic> chat;
|
||||
final Map<dynamic, dynamic>? message;
|
||||
|
||||
const ResolvedChat(this.chat, this.message);
|
||||
}
|
||||
|
||||
class ResolvedUser extends ResolvedLink {
|
||||
final Map<dynamic, dynamic> contact;
|
||||
|
||||
const ResolvedUser(this.contact);
|
||||
}
|
||||
|
||||
class ResolvedLinkError extends ResolvedLink {
|
||||
final String message;
|
||||
|
||||
const ResolvedLinkError(this.message);
|
||||
}
|
||||
|
||||
abstract class LinkModule {
|
||||
static Future<ResolvedLink?> resolve(Api api, String url) async {
|
||||
final Packet response;
|
||||
try {
|
||||
response = await api.sendRequest(Opcode.linkInfo, {'link': url});
|
||||
} on TimeoutException {
|
||||
return const ResolvedLinkError('Превышено время ожидания');
|
||||
} on PacketError catch (e) {
|
||||
return ResolvedLinkError(e.message);
|
||||
}
|
||||
|
||||
final payload = response.payload;
|
||||
if (payload is! Map) return null;
|
||||
if (!response.isOk) {
|
||||
return ResolvedLinkError(messageFromErrorPayload(payload));
|
||||
}
|
||||
|
||||
final chat = payload['chat'];
|
||||
if (chat is Map) {
|
||||
final message = payload['message'];
|
||||
return ResolvedChat(chat, message is Map ? message : null);
|
||||
}
|
||||
|
||||
final user = payload['user'];
|
||||
if (user is Map && user['contact'] is Map) {
|
||||
return ResolvedUser(user['contact'] as Map);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<String?> join(Api api, String url) async {
|
||||
try {
|
||||
final response = await api.sendRequest(Opcode.chatJoin, {'link': url});
|
||||
if (response.isOk) return null;
|
||||
return messageFromErrorPayload(response.payload);
|
||||
} on TimeoutException {
|
||||
return 'Превышено время ожидания';
|
||||
} on PacketError catch (e) {
|
||||
return e.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user