feat: вродь диплинки и /start
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../backend/modules/chats.dart';
|
||||
import '../../backend/modules/links.dart';
|
||||
import '../../core/cache/info_cache.dart';
|
||||
import '../../core/links/max_link.dart';
|
||||
import '../../core/storage/app_database.dart';
|
||||
import '../../main.dart';
|
||||
@@ -12,7 +14,8 @@ import '../screens/contacts/open_contact_profile.dart';
|
||||
import 'call_link_handler.dart';
|
||||
import 'confirm_dialog.dart';
|
||||
import 'custom_notification.dart';
|
||||
import 'sticker_pack_sheet.dart';
|
||||
import 'max_link_nav.dart';
|
||||
import 'max_route_handler.dart';
|
||||
import 'swipe_route.dart';
|
||||
import 'web_qr_login.dart';
|
||||
|
||||
@@ -20,20 +23,47 @@ Future<bool> tryHandleMaxLink(BuildContext context, String url) async {
|
||||
final link = MaxLink.parse(url);
|
||||
if (link == null) return false;
|
||||
|
||||
if (link.kind == MaxLinkKind.call) {
|
||||
return tryHandleCallLink(context, url);
|
||||
switch (link) {
|
||||
case MaxRootLink():
|
||||
popToAppRoot(context);
|
||||
return true;
|
||||
case MaxCurrentLink():
|
||||
return true;
|
||||
case MaxAuthLink(:final url):
|
||||
await confirmAndAuthorizeWebQrLogin(context, url);
|
||||
return true;
|
||||
case MaxCallLink(:final url):
|
||||
return tryHandleCallLink(context, url);
|
||||
case MaxStickerSetLink(:final path):
|
||||
return openStickerSetByPath(context, path);
|
||||
case MaxShareSelfLink():
|
||||
return _shareOwnLink(context);
|
||||
case MaxShareTextLink(:final text):
|
||||
return shareTextToChat(context, text);
|
||||
case MaxFolderLink(:final folderId):
|
||||
return openFolderChatList(context, folderId);
|
||||
case MaxRouteLink(:final route, :final params):
|
||||
return openMaxRoute(context, route, params);
|
||||
case MaxContactIdLink(:final userId):
|
||||
return openContactById(context, userId);
|
||||
case MaxChatIdLink(:final chatId, :final messageId):
|
||||
return openChatById(context, chatId, messageId: messageId);
|
||||
case MaxWebAppLink():
|
||||
return _openWebAppLink(context, link);
|
||||
case MaxContentLink():
|
||||
return _openContentLink(context, link);
|
||||
}
|
||||
}
|
||||
|
||||
if (link.kind == MaxLinkKind.auth) {
|
||||
await confirmAndAuthorizeWebQrLogin(context, link.url);
|
||||
return true;
|
||||
}
|
||||
Future<ResolvedLink?> _resolve(String url, String baseUrl) async {
|
||||
final resolved = await LinkModule.resolve(api, url);
|
||||
if (baseUrl == url) return resolved;
|
||||
if (resolved is ResolvedChat || resolved is ResolvedUser) return resolved;
|
||||
return LinkModule.resolve(api, baseUrl);
|
||||
}
|
||||
|
||||
if (link.kind == MaxLinkKind.stickerSet) {
|
||||
return _openStickerSet(context, link.url);
|
||||
}
|
||||
|
||||
final resolved = await _resolve(link);
|
||||
Future<bool> _openContentLink(BuildContext context, MaxContentLink link) async {
|
||||
final resolved = await _resolve(link.url, link.baseUrl);
|
||||
if (!context.mounted) return true;
|
||||
|
||||
switch (resolved) {
|
||||
@@ -51,36 +81,62 @@ Future<bool> tryHandleMaxLink(BuildContext context, String url) async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ResolvedLink?> _resolve(MaxLink link) async {
|
||||
final resolved = await LinkModule.resolve(api, link.url);
|
||||
if (link.startPayload == null || link.baseUrl == link.url) return resolved;
|
||||
if (resolved is ResolvedChat || resolved is ResolvedUser) return resolved;
|
||||
return LinkModule.resolve(api, link.baseUrl);
|
||||
}
|
||||
|
||||
Future<bool> _openStickerSet(BuildContext context, String url) async {
|
||||
final path = url
|
||||
.replaceFirst(
|
||||
RegExp(r'^https?://(?:www\.)?max\.ru/', caseSensitive: false),
|
||||
'',
|
||||
)
|
||||
.split('?')
|
||||
.first
|
||||
.split('#')
|
||||
.first;
|
||||
final set = await stickersModule.resolveSetByLink(path);
|
||||
Future<bool> _openWebAppLink(BuildContext context, MaxWebAppLink link) async {
|
||||
final resolved = await _resolve(link.url, link.url);
|
||||
if (!context.mounted) return true;
|
||||
if (set == null) {
|
||||
showCustomNotification(context, 'Стикерпак недоступен');
|
||||
|
||||
final botId = await _botIdOf(resolved);
|
||||
if (!context.mounted) return true;
|
||||
if (botId == null) {
|
||||
final message = resolved is ResolvedLinkError
|
||||
? resolved.message
|
||||
: 'Не удалось открыть приложение';
|
||||
showCustomNotification(context, message);
|
||||
return true;
|
||||
}
|
||||
await showStickerPackSheet(context, knownSetId: set.id);
|
||||
return openWebAppForBot(context, botId, startParam: link.startApp);
|
||||
}
|
||||
|
||||
Future<int?> _botIdOf(ResolvedLink? resolved) async {
|
||||
switch (resolved) {
|
||||
case ResolvedUser(:final contact):
|
||||
final id = contact['id'];
|
||||
return id is int ? id : null;
|
||||
case ResolvedChat(:final chat):
|
||||
final chatId = chat['id'];
|
||||
if (chatId is! int) return null;
|
||||
if ((chat['type'] as String?) != 'DIALOG') return null;
|
||||
final myId = await currentAccountId();
|
||||
return myId == 0 ? null : chatId ^ myId;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _shareOwnLink(BuildContext context) async {
|
||||
final myId = await currentAccountId();
|
||||
if (myId == 0) return false;
|
||||
final info = await ContactInfoFetch.get(myId);
|
||||
if (!context.mounted) return true;
|
||||
|
||||
final link = (info?.raw['link'] as String?)?.trim();
|
||||
if (link == null || link.isEmpty) {
|
||||
showCustomNotification(context, 'У профиля нет публичной ссылки');
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
await Share.share(link);
|
||||
} catch (_) {
|
||||
if (context.mounted) {
|
||||
showCustomNotification(context, 'Не удалось поделиться ссылкой');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _openContact(
|
||||
BuildContext context,
|
||||
MaxLink link,
|
||||
MaxContentLink link,
|
||||
Map<dynamic, dynamic> contact,
|
||||
) async {
|
||||
final id = contact['id'];
|
||||
@@ -112,8 +168,7 @@ Future<bool> _startBotDialog(
|
||||
Map<dynamic, dynamic> contact,
|
||||
String startPayload,
|
||||
) async {
|
||||
final profile = await AppDatabase.loadActiveProfile();
|
||||
final myId = profile?.id ?? 0;
|
||||
final myId = await currentAccountId();
|
||||
if (myId == 0) return false;
|
||||
|
||||
final chatId =
|
||||
@@ -155,7 +210,7 @@ void _openChatAndStartBot(
|
||||
|
||||
Future<void> _openResolvedChat(
|
||||
BuildContext context,
|
||||
MaxLink link,
|
||||
MaxContentLink link,
|
||||
ResolvedChat resolved,
|
||||
) async {
|
||||
final chat = resolved.chat;
|
||||
@@ -170,14 +225,15 @@ Future<void> _openResolvedChat(
|
||||
final icon = (chat['baseIconUrl'] as String?) ?? '';
|
||||
final access = chat['access'];
|
||||
|
||||
final profile = await AppDatabase.loadActiveProfile();
|
||||
final myId = profile?.id ?? 0;
|
||||
final myId = await currentAccountId();
|
||||
var isMember = myId != 0 && await AppDatabase.isChatInList(myId, id);
|
||||
|
||||
await chats.cacheServerChat(chat, myId, inList: isMember);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (link.kind == MaxLinkKind.invite && access == 'PRIVATE' && !isMember) {
|
||||
if (link.kind == MaxContentKind.invite &&
|
||||
access == 'PRIVATE' &&
|
||||
!isMember) {
|
||||
final label = title.isEmpty ? 'этот чат' : '«$title»';
|
||||
final confirmed = await showConfirmDialog(
|
||||
context,
|
||||
@@ -210,6 +266,7 @@ Future<void> _openResolvedChat(
|
||||
return;
|
||||
}
|
||||
|
||||
final target = _messageTarget(link, resolved.message);
|
||||
pushSwipeable(
|
||||
context,
|
||||
(_) => ChatScreen(
|
||||
@@ -218,10 +275,26 @@ Future<void> _openResolvedChat(
|
||||
imageUrl: icon,
|
||||
chatType: type,
|
||||
channelSubscribed: type == 'CHANNEL' ? isMember : null,
|
||||
initialMessageId: target?.id,
|
||||
initialMessageTime: target?.time,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
({String id, int? time})? _messageTarget(
|
||||
MaxContentLink link,
|
||||
Map<dynamic, dynamic>? message,
|
||||
) {
|
||||
final serverId = message?['id']?.toString();
|
||||
final time = message?['time'];
|
||||
if (serverId != null && serverId.isNotEmpty) {
|
||||
return (id: serverId, time: time is int ? time : null);
|
||||
}
|
||||
final messageId = link.messageId;
|
||||
if (messageId == null) return null;
|
||||
return (id: messageId.toString(), time: null);
|
||||
}
|
||||
|
||||
String _contactName(Map<dynamic, dynamic> contact) {
|
||||
final names = contact['names'];
|
||||
if (names is List && names.isNotEmpty && names.first is Map) {
|
||||
|
||||
Reference in New Issue
Block a user