fix: задание от стасика
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class AppShowExtraInfo {
|
||||||
|
static const prefKey = 'dev_show_extra_info';
|
||||||
|
static const bool defaultValue = false;
|
||||||
|
|
||||||
|
static final ValueNotifier<bool> current = ValueNotifier(defaultValue);
|
||||||
|
|
||||||
|
static Future<bool> load() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.getBool(prefKey) ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> save(bool value) async {
|
||||||
|
current.value = value;
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setBool(prefKey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import '../../../core/utils/format.dart';
|
|||||||
import '../../../backend/modules/calls.dart';
|
import '../../../backend/modules/calls.dart';
|
||||||
import '../../widgets/komet_avatar.dart';
|
import '../../widgets/komet_avatar.dart';
|
||||||
import '../../widgets/connection_status.dart';
|
import '../../widgets/connection_status.dart';
|
||||||
|
import '../../widgets/chat_menu_overlay.dart';
|
||||||
|
|
||||||
class CallsTab extends StatefulWidget {
|
class CallsTab extends StatefulWidget {
|
||||||
const CallsTab({super.key});
|
const CallsTab({super.key});
|
||||||
@@ -206,6 +207,24 @@ class _CallsTabState extends State<CallsTab> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Builder(
|
||||||
|
builder: (btnContext) => IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.more_vert,
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
size: 20,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 36,
|
||||||
|
minHeight: 36,
|
||||||
|
),
|
||||||
|
onPressed: () => _showCallMenu(btnContext, call),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -213,6 +232,29 @@ class _CallsTabState extends State<CallsTab> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showCallMenu(BuildContext anchorContext, CallLogEntry call) {
|
||||||
|
final box = anchorContext.findRenderObject() as RenderBox?;
|
||||||
|
if (box == null || !box.hasSize) return;
|
||||||
|
final anchorRect = box.localToGlobal(Offset.zero) & box.size;
|
||||||
|
showChatMenu(
|
||||||
|
context: context,
|
||||||
|
anchorRect: anchorRect,
|
||||||
|
items: [
|
||||||
|
ChatMenuItem(
|
||||||
|
icon: Symbols.delete,
|
||||||
|
label: 'Удалить',
|
||||||
|
destructive: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ChatMenuItem(
|
||||||
|
icon: Symbols.call,
|
||||||
|
label: 'Перезвонить',
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildTabItem(String label, int index, ColorScheme cs) {
|
Widget _buildTabItem(String label, int index, ColorScheme cs) {
|
||||||
final isSelected = _selectedTabIndex == index;
|
final isSelected = _selectedTabIndex == index;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import '../../../backend/modules/messages.dart' show ContactCache;
|
import '../../../backend/modules/messages.dart' show ContactCache;
|
||||||
import '../../../core/cache/info_cache.dart';
|
import '../../../core/cache/info_cache.dart';
|
||||||
|
import '../../../core/config/app_show_extra_info.dart';
|
||||||
import '../../../core/storage/app_database.dart';
|
import '../../../core/storage/app_database.dart';
|
||||||
import '../../../core/utils/format.dart';
|
import '../../../core/utils/format.dart';
|
||||||
import '../../widgets/connection_status.dart';
|
import '../../widgets/connection_status.dart';
|
||||||
import '../../widgets/glossy_pill.dart';
|
import '../../widgets/glossy_pill.dart';
|
||||||
import '../../widgets/komet_avatar.dart';
|
import '../../widgets/komet_avatar.dart';
|
||||||
|
import '../../widgets/swipe_route.dart';
|
||||||
|
import 'chat_screen.dart';
|
||||||
|
|
||||||
class _MemberInfo {
|
class _MemberInfo {
|
||||||
final int id;
|
final int id;
|
||||||
@@ -34,12 +37,15 @@ class ChatInfoScreen extends StatefulWidget {
|
|||||||
final String imageUrl;
|
final String imageUrl;
|
||||||
final String chatType;
|
final String chatType;
|
||||||
|
|
||||||
|
final int? dialogPeerId;
|
||||||
|
|
||||||
const ChatInfoScreen({
|
const ChatInfoScreen({
|
||||||
super.key,
|
super.key,
|
||||||
required this.chatId,
|
required this.chatId,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.imageUrl,
|
required this.imageUrl,
|
||||||
required this.chatType,
|
required this.chatType,
|
||||||
|
this.dialogPeerId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -81,11 +87,26 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> get _tabs {
|
List<String> get _tabs {
|
||||||
|
final showInfo = AppShowExtraInfo.current.value;
|
||||||
switch (widget.chatType) {
|
switch (widget.chatType) {
|
||||||
case 'DIALOG':
|
case 'DIALOG':
|
||||||
return _isBot
|
if (_isBot) {
|
||||||
? ['Info', 'Медиа', 'Файлы', 'Голосовые', 'Ссылки']
|
return [
|
||||||
: ['Общие чаты', 'Медиа', 'Info', 'Файлы', 'Голосовые', 'Ссылки'];
|
if (showInfo) 'Info',
|
||||||
|
'Медиа',
|
||||||
|
'Файлы',
|
||||||
|
'Голосовые',
|
||||||
|
'Ссылки',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'Общие чаты',
|
||||||
|
'Медиа',
|
||||||
|
if (showInfo) 'Info',
|
||||||
|
'Файлы',
|
||||||
|
'Голосовые',
|
||||||
|
'Ссылки',
|
||||||
|
];
|
||||||
case 'CHAT':
|
case 'CHAT':
|
||||||
return ['Участники', 'Info', 'Медиа', 'Файлы', 'Голосовые', 'Ссылки'];
|
return ['Участники', 'Info', 'Медиа', 'Файлы', 'Голосовые', 'Ссылки'];
|
||||||
case 'CHANNEL':
|
case 'CHANNEL':
|
||||||
@@ -101,19 +122,18 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
|
|
||||||
final info = await ChatInfoFetch.get(widget.chatId);
|
final info = await ChatInfoFetch.get(widget.chatId);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (info == null) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_chatData = info;
|
_chatData = info;
|
||||||
|
|
||||||
if (widget.chatType == 'DIALOG') {
|
if (widget.chatType == 'DIALOG') {
|
||||||
final parts = _chatData!['participants'] as Map? ?? {};
|
_otherId = widget.dialogPeerId;
|
||||||
for (final key in parts.keys) {
|
if (_otherId == null && info != null) {
|
||||||
final id = key is int ? key : int.tryParse(key.toString());
|
final parts = info['participants'] as Map? ?? {};
|
||||||
if (id != null && id != _myId) {
|
for (final key in parts.keys) {
|
||||||
_otherId = id;
|
final id = key is int ? key : int.tryParse(key.toString());
|
||||||
break;
|
if (id != null && id != _myId) {
|
||||||
|
_otherId = id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +153,9 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
_isOnline = st == 1;
|
_isOnline = st == 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (info == null) {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
return;
|
||||||
} else if (widget.chatType == 'CHAT') {
|
} else if (widget.chatType == 'CHAT') {
|
||||||
final parts = _chatData!['participants'] as Map? ?? {};
|
final parts = _chatData!['participants'] as Map? ?? {};
|
||||||
final admins = _chatData!['adminParticipants'] as Map? ?? {};
|
final admins = _chatData!['adminParticipants'] as Map? ?? {};
|
||||||
@@ -186,11 +209,9 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cs = Theme.of(context).colorScheme;
|
final cs = Theme.of(context).colorScheme;
|
||||||
final isDark = cs.brightness == Brightness.dark;
|
|
||||||
final bg = isDark ? Colors.black : cs.surface;
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: bg,
|
backgroundColor: cs.surface,
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||||
floatingActionButton: const ConnectionSpinner(),
|
floatingActionButton: const ConnectionSpinner(),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
@@ -292,31 +313,31 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
// ─── ACTION BUTTONS ──────────────────────────────────────────────────────
|
// ─── ACTION BUTTONS ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
Widget _buildActions(ColorScheme cs) {
|
Widget _buildActions(ColorScheme cs) {
|
||||||
final List<({IconData icon, String label})> btns;
|
final List<({IconData icon, String label, VoidCallback? onTap})> btns;
|
||||||
|
|
||||||
if (widget.chatType == 'DIALOG') {
|
if (widget.chatType == 'DIALOG') {
|
||||||
if (_isBot) {
|
if (_isBot) {
|
||||||
btns = [
|
btns = [
|
||||||
(icon: Icons.chat_bubble, label: 'Чат'),
|
(icon: Icons.chat_bubble, label: 'Чат', onTap: _openChat),
|
||||||
(icon: Icons.notifications, label: 'Звук'),
|
(icon: Icons.notifications, label: 'Звук', onTap: null),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
btns = [
|
btns = [
|
||||||
(icon: Icons.chat_bubble, label: 'Чат'),
|
(icon: Icons.chat_bubble, label: 'Чат', onTap: _openChat),
|
||||||
(icon: Icons.notifications, label: 'Звук'),
|
(icon: Icons.notifications, label: 'Звук', onTap: null),
|
||||||
(icon: Icons.call, label: 'Звонок'),
|
(icon: Icons.call, label: 'Звонок', onTap: null),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else if (widget.chatType == 'CHANNEL') {
|
} else if (widget.chatType == 'CHANNEL') {
|
||||||
btns = [
|
btns = [
|
||||||
(icon: Icons.notifications, label: 'Звук'),
|
(icon: Icons.notifications, label: 'Звук', onTap: null),
|
||||||
(icon: Icons.exit_to_app, label: 'Покинуть'),
|
(icon: Icons.exit_to_app, label: 'Покинуть', onTap: null),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
btns = [
|
btns = [
|
||||||
(icon: Icons.chat_bubble, label: 'Чат'),
|
(icon: Icons.chat_bubble, label: 'Чат', onTap: null),
|
||||||
(icon: Icons.notifications, label: 'Звук'),
|
(icon: Icons.notifications, label: 'Звук', onTap: null),
|
||||||
(icon: Icons.exit_to_app, label: 'Покинуть'),
|
(icon: Icons.exit_to_app, label: 'Покинуть', onTap: null),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +346,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < btns.length; i++) ...[
|
for (int i = 0; i < btns.length; i++) ...[
|
||||||
_actionBtn(cs, btns[i].icon, btns[i].label),
|
_actionBtn(cs, btns[i].icon, btns[i].label, btns[i].onTap),
|
||||||
if (i < btns.length - 1) const SizedBox(width: 8),
|
if (i < btns.length - 1) const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -333,9 +354,27 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionBtn(ColorScheme cs, IconData icon, String label) {
|
void _openChat() {
|
||||||
|
pushSwipeable(
|
||||||
|
context,
|
||||||
|
(_) => ChatScreen(
|
||||||
|
chatId: widget.chatId,
|
||||||
|
name: widget.name,
|
||||||
|
imageUrl: widget.imageUrl,
|
||||||
|
chatType: 'DIALOG',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _actionBtn(
|
||||||
|
ColorScheme cs,
|
||||||
|
IconData icon,
|
||||||
|
String label, [
|
||||||
|
VoidCallback? onTap,
|
||||||
|
]) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: GlossyPill(
|
child: GlossyPill(
|
||||||
|
onTap: onTap,
|
||||||
color: cs.surfaceContainerHigh,
|
color: cs.surfaceContainerHigh,
|
||||||
borderRadius: BorderRadius.circular(14),
|
borderRadius: BorderRadius.circular(14),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
@@ -377,6 +416,13 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
_simpleInfoCard(cs, 'Номер телефона', formatPhone(phoneInt)!),
|
_simpleInfoCard(cs, 'Номер телефона', formatPhone(phoneInt)!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
final bio =
|
||||||
|
(_contactData?['description'] as String?) ??
|
||||||
|
(_contactData?['about'] as String?);
|
||||||
|
if (bio != null && bio.isNotEmpty) {
|
||||||
|
if (items.isNotEmpty) items.add(const SizedBox(height: 8));
|
||||||
|
items.add(_simpleInfoCard(cs, 'О себе', bio));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (widget.chatType == 'CHANNEL') {
|
} else if (widget.chatType == 'CHANNEL') {
|
||||||
final link = _chatData?['link'] as String?;
|
final link = _chatData?['link'] as String?;
|
||||||
@@ -645,17 +691,6 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
|||||||
Widget _buildInfoTabContent(ColorScheme cs) {
|
Widget _buildInfoTabContent(ColorScheme cs) {
|
||||||
final items = <Widget>[];
|
final items = <Widget>[];
|
||||||
|
|
||||||
if (widget.chatType == 'DIALOG' && !_isBot) {
|
|
||||||
final bio =
|
|
||||||
(_contactData?['description'] as String?) ??
|
|
||||||
(_contactData?['about'] as String?);
|
|
||||||
if (bio != null && bio.isNotEmpty) {
|
|
||||||
items
|
|
||||||
..add(_infoCard(cs, 'О себе', bio))
|
|
||||||
..add(const SizedBox(height: 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.chatType == 'CHAT') {
|
if (widget.chatType == 'CHAT') {
|
||||||
final desc = _chatData?['description'] as String?;
|
final desc = _chatData?['description'] as String?;
|
||||||
if (desc != null && desc.isNotEmpty) {
|
if (desc != null && desc.isNotEmpty) {
|
||||||
|
|||||||
@@ -3,14 +3,41 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import '../../../core/protocol/opcode_map.dart';
|
import '../../../core/protocol/opcode_map.dart';
|
||||||
import '../../../core/protocol/packet.dart';
|
import '../../../core/protocol/packet.dart';
|
||||||
import '../../../core/storage/app_database.dart';
|
import '../../../core/storage/app_database.dart';
|
||||||
|
import '../../../core/storage/token_storage.dart';
|
||||||
import '../../../backend/modules/contacts.dart';
|
import '../../../backend/modules/contacts.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
import '../../widgets/komet_avatar.dart';
|
import '../../widgets/komet_avatar.dart';
|
||||||
import '../../widgets/connection_status.dart';
|
import '../../widgets/connection_status.dart';
|
||||||
import '../../widgets/sheet_helpers.dart';
|
import '../../widgets/sheet_helpers.dart';
|
||||||
import 'contact_profile_screen.dart';
|
import '../chats/chat_info_screen.dart';
|
||||||
import 'nfc_exchange_sheet.dart';
|
import 'nfc_exchange_sheet.dart';
|
||||||
|
|
||||||
|
Future<void> openContactDialogProfile(
|
||||||
|
BuildContext context, {
|
||||||
|
required int contactId,
|
||||||
|
required String name,
|
||||||
|
String? avatarUrl,
|
||||||
|
}) async {
|
||||||
|
final accountId = await TokenStorage.getActiveAccountId();
|
||||||
|
final existing = accountId == null
|
||||||
|
? null
|
||||||
|
: await AppDatabase.findDialogChatByParticipant(accountId, contactId);
|
||||||
|
final chatId = existing ?? ((accountId ?? 0) ^ contactId);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => ChatInfoScreen(
|
||||||
|
chatId: chatId,
|
||||||
|
name: name,
|
||||||
|
imageUrl: avatarUrl ?? '',
|
||||||
|
chatType: 'DIALOG',
|
||||||
|
dialogPeerId: contactId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class ContactsTab extends StatefulWidget {
|
class ContactsTab extends StatefulWidget {
|
||||||
const ContactsTab({super.key});
|
const ContactsTab({super.key});
|
||||||
|
|
||||||
@@ -100,18 +127,12 @@ class _ContactsTabState extends State<ContactsTab> {
|
|||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () => openContactDialogProfile(
|
||||||
Navigator.push(
|
context,
|
||||||
context,
|
contactId: contact.id,
|
||||||
MaterialPageRoute(
|
name: nameToDisplay,
|
||||||
builder: (_) => ContactProfileScreen(
|
avatarUrl: contact.baseUrl,
|
||||||
contactId: contact.id,
|
),
|
||||||
initialName: nameToDisplay,
|
|
||||||
initialAvatarUrl: contact.baseUrl,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -311,13 +332,21 @@ class _SearchContactSheetState extends State<_SearchContactSheet> {
|
|||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final navigator = Navigator.of(context);
|
final navigator = Navigator.of(context);
|
||||||
|
final accountId = await TokenStorage.getActiveAccountId();
|
||||||
|
final existing = accountId == null
|
||||||
|
? null
|
||||||
|
: await AppDatabase.findDialogChatByParticipant(accountId, id);
|
||||||
|
final chatId = existing ?? ((accountId ?? 0) ^ id);
|
||||||
|
if (!mounted) return;
|
||||||
navigator.pop();
|
navigator.pop();
|
||||||
navigator.push(
|
navigator.push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => ContactProfileScreen(
|
builder: (_) => ChatInfoScreen(
|
||||||
contactId: id,
|
chatId: chatId,
|
||||||
initialName: name,
|
name: name ?? 'User #$id',
|
||||||
initialAvatarUrl: raw['baseUrl'] as String?,
|
imageUrl: raw['baseUrl'] as String? ?? '',
|
||||||
|
chatType: 'DIALOG',
|
||||||
|
dialogPeerId: id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../../../core/config/app_pranks.dart';
|
|||||||
import '../../../core/config/app_stories.dart';
|
import '../../../core/config/app_stories.dart';
|
||||||
import '../../../core/config/app_commands.dart';
|
import '../../../core/config/app_commands.dart';
|
||||||
import '../../../core/config/app_link_preview.dart';
|
import '../../../core/config/app_link_preview.dart';
|
||||||
|
import '../../../core/config/app_show_extra_info.dart';
|
||||||
import '../../../core/config/app_digital_id_mode.dart';
|
import '../../../core/config/app_digital_id_mode.dart';
|
||||||
import '../../../core/config/app_media_cache.dart';
|
import '../../../core/config/app_media_cache.dart';
|
||||||
import '../../../core/protocol/opcode_map.dart';
|
import '../../../core/protocol/opcode_map.dart';
|
||||||
@@ -905,6 +906,66 @@ class _DebugMenuScreenState extends State<DebugMenuScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||||||
|
child: ValueListenableBuilder<bool>(
|
||||||
|
valueListenable: AppShowExtraInfo.current,
|
||||||
|
builder: (context, extraInfoOn, _) {
|
||||||
|
return GlossyPill(
|
||||||
|
color: cs.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
depth: 6,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 17,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.info,
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
size: 22,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Доп. информация',
|
||||||
|
style: TextStyle(
|
||||||
|
color: cs.onSurface,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
'Раздел «Info» в настройках и вкладка с '
|
||||||
|
'технической информацией в профиле собеседника',
|
||||||
|
style: TextStyle(
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Switch(
|
||||||
|
value: extraInfoOn,
|
||||||
|
onChanged: (v) {
|
||||||
|
AppShowExtraInfo.save(v);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
import '../../../core/utils/haptics.dart';
|
||||||
import '../../widgets/connection_status.dart';
|
import '../../widgets/connection_status.dart';
|
||||||
|
|
||||||
import '../../widgets/glossy_pill.dart';
|
import '../../widgets/glossy_pill.dart';
|
||||||
@@ -19,8 +20,15 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
|||||||
bool _personalChatsEnabled = true;
|
bool _personalChatsEnabled = true;
|
||||||
bool _groupsEnabled = true;
|
bool _groupsEnabled = true;
|
||||||
bool _channelsEnabled = true;
|
bool _channelsEnabled = true;
|
||||||
|
bool _hapticsEnabled = Haptics.enabled;
|
||||||
String _selectedSound = 'По умолчанию';
|
String _selectedSound = 'По умолчанию';
|
||||||
|
|
||||||
|
Future<void> _setHaptics(bool value) async {
|
||||||
|
await Haptics.setEnabled(value);
|
||||||
|
if (value) Haptics.success();
|
||||||
|
if (mounted) setState(() => _hapticsEnabled = value);
|
||||||
|
}
|
||||||
|
|
||||||
static const List<String> _sounds = [
|
static const List<String> _sounds = [
|
||||||
'По умолчанию',
|
'По умолчанию',
|
||||||
'Колокольчик',
|
'Колокольчик',
|
||||||
@@ -162,6 +170,22 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
|||||||
onTap: _pickSound,
|
onTap: _pickSound,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const SectionHeader(
|
||||||
|
'Тактильная отдача',
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 0, 8, 8),
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
_card(cs, [
|
||||||
|
_toggleRow(
|
||||||
|
cs,
|
||||||
|
icon: Symbols.vibration,
|
||||||
|
label: 'Тактильная отдача',
|
||||||
|
subtitle: 'Виброотклик при действиях в приложении',
|
||||||
|
value: _hapticsEnabled,
|
||||||
|
onChanged: _setHaptics,
|
||||||
|
),
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import '../../../backend/modules/chats.dart';
|
|||||||
import '../../../backend/modules/messages.dart';
|
import '../../../backend/modules/messages.dart';
|
||||||
import '../../../core/cache/self_presence.dart';
|
import '../../../core/cache/self_presence.dart';
|
||||||
import '../../../core/config/komet_settings.dart';
|
import '../../../core/config/komet_settings.dart';
|
||||||
|
import '../../../core/config/app_show_extra_info.dart';
|
||||||
import '../../../core/storage/app_database.dart';
|
import '../../../core/storage/app_database.dart';
|
||||||
import '../../../core/storage/token_storage.dart';
|
import '../../../core/storage/token_storage.dart';
|
||||||
import '../../../core/utils/format.dart';
|
import '../../../core/utils/format.dart';
|
||||||
import '../../../core/utils/haptics.dart';
|
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
import '../../widgets/connection_status.dart';
|
import '../../widgets/connection_status.dart';
|
||||||
@@ -27,7 +27,6 @@ import '../digital_id/digital_id_web_screen.dart';
|
|||||||
import '../webapp/web_app_screen.dart';
|
import '../webapp/web_app_screen.dart';
|
||||||
import 'cloud_storage_screen.dart';
|
import 'cloud_storage_screen.dart';
|
||||||
import 'customization_screen.dart';
|
import 'customization_screen.dart';
|
||||||
import 'performance_screen.dart';
|
|
||||||
import 'debug_menu_screen.dart';
|
import 'debug_menu_screen.dart';
|
||||||
import 'devices_screen.dart';
|
import 'devices_screen.dart';
|
||||||
import 'edit_profile_screen.dart';
|
import 'edit_profile_screen.dart';
|
||||||
@@ -52,7 +51,6 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
int _versionSecretTapCount = 0;
|
int _versionSecretTapCount = 0;
|
||||||
Timer? _versionSecretTapResetTimer;
|
Timer? _versionSecretTapResetTimer;
|
||||||
StreamSubscription? _profileUpdateSub;
|
StreamSubscription? _profileUpdateSub;
|
||||||
bool _hapticsEnabled = Haptics.enabled;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -106,13 +104,6 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setHaptics(bool value) async {
|
|
||||||
await Haptics.setEnabled(value);
|
|
||||||
// Let the user *feel* the confirmation the instant they switch it on.
|
|
||||||
if (value) Haptics.success();
|
|
||||||
if (mounted) setState(() => _hapticsEnabled = value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _openCloudStorage(BuildContext context) async {
|
Future<void> _openCloudStorage(BuildContext context) async {
|
||||||
final cs = Theme.of(context).colorScheme;
|
final cs = Theme.of(context).colorScheme;
|
||||||
final ok = await showInfoActionSheet(
|
final ok = await showInfoActionSheet(
|
||||||
@@ -256,67 +247,60 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||||
child: _buildSection(
|
child: ValueListenableBuilder<bool>(
|
||||||
context,
|
valueListenable: AppShowExtraInfo.current,
|
||||||
cs,
|
builder: (context, showExtraInfo, _) {
|
||||||
items: [
|
return _buildSection(
|
||||||
_SettingsItem(
|
context,
|
||||||
icon: Symbols.badge,
|
cs,
|
||||||
label: 'Цифровой ID',
|
items: [
|
||||||
onTap: () {
|
_SettingsItem(
|
||||||
Navigator.push(
|
icon: Symbols.badge,
|
||||||
context,
|
label: 'Цифровой ID',
|
||||||
MaterialPageRoute(
|
onTap: () {
|
||||||
builder: (context) =>
|
Navigator.push(
|
||||||
AppDigitalIdNative.current.value ||
|
context,
|
||||||
!webViewSupported
|
MaterialPageRoute(
|
||||||
? const DigitalIdScreen()
|
builder: (context) =>
|
||||||
: const DigitalIdWebScreen(),
|
AppDigitalIdNative.current.value ||
|
||||||
|
!webViewSupported
|
||||||
|
? const DigitalIdScreen()
|
||||||
|
: const DigitalIdWebScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_SettingsItem(
|
||||||
|
icon: Symbols.language,
|
||||||
|
label: 'Войти в Сферум',
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => WebAppScreen(
|
||||||
|
title: 'Сферум',
|
||||||
|
loader: () => webAppModule.fetchSferum(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (showExtraInfo)
|
||||||
|
_SettingsItem(
|
||||||
|
icon: Symbols.info,
|
||||||
|
label: 'Info',
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const InfoScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
_SettingsItem(
|
|
||||||
icon: Symbols.language,
|
|
||||||
label: 'Войти в Сферум',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => WebAppScreen(
|
|
||||||
title: 'Сферум',
|
|
||||||
loader: () => webAppModule.fetchSferum(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_SettingsItem(
|
|
||||||
// Иконку кометы блять дайте!!!!!!!1
|
|
||||||
icon: Symbols.auto_awesome,
|
|
||||||
label: 'Komet',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const KometSettingsScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_SettingsItem(
|
|
||||||
icon: Symbols.info,
|
|
||||||
label: 'Info',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const InfoScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -339,18 +323,6 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_SettingsItem(
|
|
||||||
icon: Symbols.speed,
|
|
||||||
label: 'Производительность',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const PerformanceScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -374,12 +346,6 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_SettingsItem(
|
|
||||||
icon: Symbols.vibration,
|
|
||||||
label: 'Тактильная отдача',
|
|
||||||
toggleValue: _hapticsEnabled,
|
|
||||||
onToggle: _setHaptics,
|
|
||||||
),
|
|
||||||
_SettingsItem(
|
_SettingsItem(
|
||||||
icon: Symbols.cloud,
|
icon: Symbols.cloud,
|
||||||
label: 'Облачное хранилище [BETA]',
|
label: 'Облачное хранилище [BETA]',
|
||||||
@@ -506,6 +472,18 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
tintColor: cs.error,
|
tintColor: cs.error,
|
||||||
onTap: _confirmLogout,
|
onTap: _confirmLogout,
|
||||||
),
|
),
|
||||||
|
_SettingsItem(
|
||||||
|
icon: Symbols.auto_awesome,
|
||||||
|
label: 'Komet',
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const KometSettingsScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -737,9 +715,7 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
Material(
|
Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: item.isToggle
|
onTap: item.onTap ?? () {},
|
||||||
? () => item.onToggle!(!(item.toggleValue ?? false))
|
|
||||||
: (item.onTap ?? () {}),
|
|
||||||
borderRadius: isLast
|
borderRadius: isLast
|
||||||
? const BorderRadius.vertical(bottom: Radius.circular(20))
|
? const BorderRadius.vertical(bottom: Radius.circular(20))
|
||||||
: null,
|
: null,
|
||||||
@@ -764,18 +740,12 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (item.isToggle)
|
Icon(
|
||||||
Switch.adaptive(
|
Symbols.chevron_right,
|
||||||
value: item.toggleValue ?? false,
|
color: cs.outline,
|
||||||
onChanged: item.onToggle,
|
size: 20,
|
||||||
)
|
weight: 400,
|
||||||
else
|
),
|
||||||
Icon(
|
|
||||||
Symbols.chevron_right,
|
|
||||||
color: cs.outline,
|
|
||||||
size: 20,
|
|
||||||
weight: 400,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -801,21 +771,12 @@ class _SettingsItem {
|
|||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
final Color? tintColor;
|
final Color? tintColor;
|
||||||
|
|
||||||
/// When [onToggle] is set the row renders a trailing switch instead of a
|
|
||||||
/// chevron, and [toggleValue] reflects its current state.
|
|
||||||
final bool? toggleValue;
|
|
||||||
final ValueChanged<bool>? onToggle;
|
|
||||||
|
|
||||||
const _SettingsItem({
|
const _SettingsItem({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.label,
|
required this.label,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.tintColor,
|
this.tintColor,
|
||||||
this.toggleValue,
|
|
||||||
this.onToggle,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bool get isToggle => onToggle != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PhoneSpoiler extends StatefulWidget {
|
class _PhoneSpoiler extends StatefulWidget {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class _ChatMenuLayerState extends State<_ChatMenuLayer>
|
|||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
static const double _menuWidth = 290.0;
|
static const double _menuWidth = 290.0;
|
||||||
static const double _hMargin = 8.0;
|
static const double _hMargin = 8.0;
|
||||||
|
static const double _vMargin = 8.0;
|
||||||
static const double _gap = 6.0;
|
static const double _gap = 6.0;
|
||||||
|
|
||||||
late final AnimationController _animController;
|
late final AnimationController _animController;
|
||||||
@@ -104,17 +105,24 @@ class _ChatMenuLayerState extends State<_ChatMenuLayer>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rect _resolveRect(Size screen) {
|
Rect _resolveRect(Size screen) {
|
||||||
double left = widget.anchorRect.right - _menuWidth;
|
final maxWidth = screen.width - 2 * _hMargin;
|
||||||
left = left.clamp(_hMargin, screen.width - _menuWidth - _hMargin);
|
final width = maxWidth <= 0 ? screen.width : (_menuWidth.clamp(0.0, maxWidth));
|
||||||
double top = widget.anchorRect.bottom + _gap;
|
final maxLeft = screen.width - width - _hMargin;
|
||||||
return Rect.fromLTWH(left, top, _menuWidth, 0);
|
double left = widget.anchorRect.right - width;
|
||||||
|
if (left > maxLeft) left = maxLeft;
|
||||||
|
if (left < _hMargin) left = _hMargin;
|
||||||
|
final top = widget.anchorRect.bottom + _gap;
|
||||||
|
return Rect.fromLTWH(left, top, width, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cs = Theme.of(context).colorScheme;
|
final cs = Theme.of(context).colorScheme;
|
||||||
final screen = MediaQuery.sizeOf(context);
|
final screen = MediaQuery.sizeOf(context);
|
||||||
|
final bottomInset = MediaQuery.paddingOf(context).bottom;
|
||||||
final rect = _resolveRect(screen);
|
final rect = _resolveRect(screen);
|
||||||
|
final maxHeight = (screen.height - rect.top - bottomInset - _vMargin)
|
||||||
|
.clamp(120.0, double.infinity);
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: _animation,
|
animation: _animation,
|
||||||
builder: (ctx, child) {
|
builder: (ctx, child) {
|
||||||
@@ -151,21 +159,26 @@ class _ChatMenuLayerState extends State<_ChatMenuLayer>
|
|||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
elevation: 12,
|
elevation: 12,
|
||||||
shadowColor: Colors.black.withValues(alpha: 0.45),
|
shadowColor: Colors.black.withValues(alpha: 0.45),
|
||||||
child: Column(
|
child: ConstrainedBox(
|
||||||
mainAxisSize: MainAxisSize.min,
|
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
const SizedBox(height: 6),
|
child: Column(
|
||||||
for (final item in widget.items) ...[
|
mainAxisSize: MainAxisSize.min,
|
||||||
_ChatMenuRow(item: item, onTap: () => _onItemTap(item)),
|
children: [
|
||||||
if (item.dividerAfter)
|
const SizedBox(height: 6),
|
||||||
Divider(
|
for (final item in widget.items) ...[
|
||||||
height: 1,
|
_ChatMenuRow(item: item, onTap: () => _onItemTap(item)),
|
||||||
thickness: 1,
|
if (item.dividerAfter)
|
||||||
color: cs.onSurface.withValues(alpha: 0.07),
|
Divider(
|
||||||
),
|
height: 1,
|
||||||
],
|
thickness: 1,
|
||||||
const SizedBox(height: 6),
|
color: cs.onSurface.withValues(alpha: 0.07),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import 'core/storage/app_instance.dart';
|
|||||||
import 'core/storage/draft_store.dart';
|
import 'core/storage/draft_store.dart';
|
||||||
import 'core/config/app_accent.dart';
|
import 'core/config/app_accent.dart';
|
||||||
import 'core/config/app_amoled.dart';
|
import 'core/config/app_amoled.dart';
|
||||||
|
import 'core/config/app_show_extra_info.dart';
|
||||||
import 'core/config/app_bubble_behavior.dart';
|
import 'core/config/app_bubble_behavior.dart';
|
||||||
import 'core/config/komet_settings.dart';
|
import 'core/config/komet_settings.dart';
|
||||||
import 'core/config/app_bubble_shape.dart';
|
import 'core/config/app_bubble_shape.dart';
|
||||||
@@ -126,6 +127,7 @@ void main() async {
|
|||||||
final linkPreviewFuture = AppLinkPreview.load();
|
final linkPreviewFuture = AppLinkPreview.load();
|
||||||
final cacheLimitFuture = AppMediaCacheLimit.load();
|
final cacheLimitFuture = AppMediaCacheLimit.load();
|
||||||
final digitalIdNativeFuture = AppDigitalIdNative.load();
|
final digitalIdNativeFuture = AppDigitalIdNative.load();
|
||||||
|
final showExtraInfoFuture = AppShowExtraInfo.load();
|
||||||
|
|
||||||
final packageInfo = await packageInfoFuture;
|
final packageInfo = await packageInfoFuture;
|
||||||
isOnemeFlavor = packageInfo.packageName == 'ru.oneme.app';
|
isOnemeFlavor = packageInfo.packageName == 'ru.oneme.app';
|
||||||
@@ -170,6 +172,7 @@ void main() async {
|
|||||||
AppLinkPreview.current.value = await linkPreviewFuture;
|
AppLinkPreview.current.value = await linkPreviewFuture;
|
||||||
AppMediaCacheLimit.current.value = await cacheLimitFuture;
|
AppMediaCacheLimit.current.value = await cacheLimitFuture;
|
||||||
AppDigitalIdNative.current.value = await digitalIdNativeFuture;
|
AppDigitalIdNative.current.value = await digitalIdNativeFuture;
|
||||||
|
AppShowExtraInfo.current.value = await showExtraInfoFuture;
|
||||||
runApp(
|
runApp(
|
||||||
KometApp(
|
KometApp(
|
||||||
initialLocale: initialLocale,
|
initialLocale: initialLocale,
|
||||||
|
|||||||
Reference in New Issue
Block a user