Compare commits
No commits in common. "908f0cb59ec0546d05e11b09372e24908e01e2c3" and "a1c4e5eca04cef112f096a66953fbafcab12457f" have entirely different histories.
908f0cb59e
...
a1c4e5eca0
Binary file not shown.
@ -939,7 +939,5 @@
|
|||||||
"settingsSoundEffects": "Sound Effects",
|
"settingsSoundEffects": "Sound Effects",
|
||||||
"settingsSoundEffectsDescription": "Enable the sound effects around the app.",
|
"settingsSoundEffectsDescription": "Enable the sound effects around the app.",
|
||||||
"settingsResetMemorizedWindowSize": "Reset Window Size",
|
"settingsResetMemorizedWindowSize": "Reset Window Size",
|
||||||
"settingsResetMemorizedWindowSizeDescription": "Reset the memorized window size, and set it to the default size.",
|
"settingsResetMemorizedWindowSizeDescription": "Reset the memorized window size, and set it to the default size."
|
||||||
"chatDirect": "Direct Messages",
|
|
||||||
"back": "返回"
|
|
||||||
}
|
}
|
||||||
|
@ -936,7 +936,5 @@
|
|||||||
"settingsSoundEffects": "声音效果",
|
"settingsSoundEffects": "声音效果",
|
||||||
"settingsSoundEffectsDescription": "在一些场合下启用声音特效。",
|
"settingsSoundEffectsDescription": "在一些场合下启用声音特效。",
|
||||||
"settingsResetMemorizedWindowSize": "重置窗口大小",
|
"settingsResetMemorizedWindowSize": "重置窗口大小",
|
||||||
"settingsResetMemorizedWindowSizeDescription": "重置记忆的窗口大小,以重新设置为默认大小。",
|
"settingsResetMemorizedWindowSizeDescription": "重置记忆的窗口大小,以重新设置为默认大小。"
|
||||||
"chatDirect": "私信",
|
|
||||||
"back": "返回"
|
|
||||||
}
|
}
|
||||||
|
@ -396,8 +396,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
final cfg = context.read<ConfigProvider>();
|
final cfg = context.read<ConfigProvider>();
|
||||||
if (!cfg.soundEffects) return;
|
if (!cfg.soundEffects) return;
|
||||||
|
|
||||||
final player = AudioPlayer(playerId: 'launch-done-player');
|
final player = AudioPlayer(playerId: 'launch-intro-player');
|
||||||
await player.play(AssetSource('audio/sfx/launch-done.mp3'), volume: 0.8);
|
await player.play(AssetSource('audio/sfx/launch-intro.mp3'), volume: 0.5);
|
||||||
player.onPlayerComplete.listen((_) {
|
player.onPlayerComplete.listen((_) {
|
||||||
player.dispose();
|
player.dispose();
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ const kNetworkServerStoreKey = 'app_server_url';
|
|||||||
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
||||||
const kAppBackgroundStoreKey = 'app_has_background';
|
const kAppBackgroundStoreKey = 'app_has_background';
|
||||||
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
||||||
|
const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse';
|
||||||
const kAppNotifyWithHaptic = 'app_notify_with_haptic';
|
const kAppNotifyWithHaptic = 'app_notify_with_haptic';
|
||||||
const kAppExpandPostLink = 'app_expand_post_link';
|
const kAppExpandPostLink = 'app_expand_post_link';
|
||||||
const kAppExpandChatLink = 'app_expand_chat_link';
|
const kAppExpandChatLink = 'app_expand_chat_link';
|
||||||
@ -46,17 +47,27 @@ class ConfigProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool drawerIsCollapsed = false;
|
bool drawerIsCollapsed = false;
|
||||||
|
bool drawerIsExpanded = false;
|
||||||
|
|
||||||
void calcDrawerSize(BuildContext context, {bool withMediaQuery = false}) {
|
void calcDrawerSize(BuildContext context, {bool withMediaQuery = false}) {
|
||||||
bool newDrawerIsCollapsed = false;
|
bool newDrawerIsCollapsed = false;
|
||||||
|
bool newDrawerIsExpanded = false;
|
||||||
if (withMediaQuery) {
|
if (withMediaQuery) {
|
||||||
newDrawerIsCollapsed = MediaQuery.of(context).size.width < 600;
|
newDrawerIsCollapsed = MediaQuery.of(context).size.width < 600;
|
||||||
|
newDrawerIsExpanded = MediaQuery.of(context).size.width >= 601;
|
||||||
} else {
|
} else {
|
||||||
final rpb = ResponsiveBreakpoints.of(context);
|
final rpb = ResponsiveBreakpoints.of(context);
|
||||||
newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
||||||
|
newDrawerIsExpanded = rpb.largerThan(TABLET)
|
||||||
|
? (prefs.getBool(kAppDrawerPreferCollapse) ?? false)
|
||||||
|
? false
|
||||||
|
: true
|
||||||
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newDrawerIsCollapsed != drawerIsCollapsed) {
|
if (newDrawerIsExpanded != drawerIsExpanded ||
|
||||||
|
newDrawerIsCollapsed != drawerIsCollapsed) {
|
||||||
|
drawerIsExpanded = newDrawerIsExpanded;
|
||||||
drawerIsCollapsed = newDrawerIsCollapsed;
|
drawerIsCollapsed = newDrawerIsCollapsed;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
|
||||||
class AppNavListItem {
|
class AppNavListItem {
|
||||||
final String title;
|
final String title;
|
||||||
@ -134,4 +135,11 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
_currentIndex = idx;
|
_currentIndex = idx;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SnRealm? focusedRealm;
|
||||||
|
|
||||||
|
void setFocusedRealm(SnRealm? realm) {
|
||||||
|
focusedRealm = realm;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,6 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
if (now.day == 1 && now.month == 4) {
|
if (now.day == 1 && now.month == 4) {
|
||||||
_notifySoundPlayer.play(
|
_notifySoundPlayer.play(
|
||||||
AssetSource('audio/notify/metal-pipe.mp3'),
|
AssetSource('audio/notify/metal-pipe.mp3'),
|
||||||
volume: 0.6,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
||||||
@ -8,14 +6,11 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
import 'package:surface/providers/channel.dart';
|
import 'package:surface/providers/channel.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/sn_realm.dart';
|
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/account/account_select.dart';
|
import 'package:surface/widgets/account/account_select.dart';
|
||||||
import 'package:surface/widgets/app_bar_leading.dart';
|
import 'package:surface/widgets/app_bar_leading.dart';
|
||||||
@ -23,7 +18,6 @@ import 'package:surface/widgets/dialog.dart';
|
|||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/unauthorized_hint.dart';
|
import 'package:surface/widgets/unauthorized_hint.dart';
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class ChatScreen extends StatefulWidget {
|
class ChatScreen extends StatefulWidget {
|
||||||
@ -41,7 +35,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
List<SnChannel>? _channels;
|
List<SnChannel>? _channels;
|
||||||
Map<int, SnChatMessage>? _lastMessages;
|
Map<int, SnChatMessage>? _lastMessages;
|
||||||
Map<int, int>? _unreadCounts;
|
Map<int, int>? _unreadCounts;
|
||||||
Map<int, int>? _unreadCountsGrouped;
|
|
||||||
|
|
||||||
Future<void> _fetchWhatsNew() async {
|
Future<void> _fetchWhatsNew() async {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
@ -49,48 +42,19 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (resp.data == null) return;
|
if (resp.data == null) return;
|
||||||
final List<dynamic> out = resp.data;
|
final List<dynamic> out = resp.data;
|
||||||
setState(() {
|
setState(() {
|
||||||
_unreadCounts ??= {};
|
_unreadCounts = {for (var v in out) v['channel_id']: v['count']};
|
||||||
_unreadCountsGrouped ??= {};
|
|
||||||
for (var v in out) {
|
|
||||||
_unreadCounts![v['channel_id']] = v['count'];
|
|
||||||
final channel =
|
|
||||||
_channels?.firstWhereOrNull((ele) => ele.id == v['channel_id']);
|
|
||||||
if (channel != null) {
|
|
||||||
if (channel.realmId != null) {
|
|
||||||
_unreadCountsGrouped![channel.realmId!] ??= 0;
|
|
||||||
_unreadCountsGrouped![channel.realmId!] =
|
|
||||||
(_unreadCountsGrouped![channel.realmId!]! + v['count']).toInt();
|
|
||||||
}
|
|
||||||
if (channel.type == 1) {
|
|
||||||
_unreadCountsGrouped![0] ??= 0;
|
|
||||||
_unreadCountsGrouped![0] =
|
|
||||||
(_unreadCountsGrouped![0]! + v['count']).toInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshChannels({bool withBoost = false, bool noRemote = false}) {
|
void _refreshChannels({bool noRemote = false}) {
|
||||||
final ct = context.read<ChatChannelProvider>();
|
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!withBoost) {
|
|
||||||
if (!noRemote) {
|
|
||||||
ct.refreshAvailableChannels();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_channels = ct.availableChannels;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
final chan = context.read<ChatChannelProvider>();
|
final chan = context.read<ChatChannelProvider>();
|
||||||
chan.fetchChannels(noRemote: true).listen((channels) async {
|
chan.fetchChannels(noRemote: noRemote).listen((channels) async {
|
||||||
final lastMessages = await chan.getLastMessages(channels);
|
final lastMessages = await chan.getLastMessages(channels);
|
||||||
_lastMessages = {for (final val in lastMessages) val.channelId: val};
|
_lastMessages = {for (final val in lastMessages) val.channelId: val};
|
||||||
channels.sort((a, b) {
|
channels.sort((a, b) {
|
||||||
@ -132,7 +96,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
..onDone(() {
|
..onDone(() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
_fetchWhatsNew();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,12 +130,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshChannels(withBoost: true);
|
_refreshChannels();
|
||||||
|
_fetchWhatsNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapChannel(SnChannel channel) {
|
void _onTapChannel(SnChannel channel) {
|
||||||
setState(() => _unreadCounts?[channel.id] = 0);
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
if (ResponsiveScaffold.getIsExpand(context)) {
|
|
||||||
GoRouter.of(context).pushReplacementNamed(
|
GoRouter.of(context).pushReplacementNamed(
|
||||||
'chatRoom',
|
'chatRoom',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
@ -185,30 +148,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
_refreshChannels(noRemote: true);
|
_refreshChannels(noRemote: true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
GoRouter.of(context).pushNamed(
|
|
||||||
'chatRoom',
|
|
||||||
pathParameters: {
|
|
||||||
'scope': channel.realm?.alias ?? 'global',
|
|
||||||
'alias': channel.alias,
|
|
||||||
},
|
|
||||||
).then((value) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _unreadCounts?[channel.id] = 0);
|
|
||||||
_refreshChannels(noRemote: true);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SnRealm? _focusedRealm;
|
|
||||||
bool _isDirect = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
final rel = context.read<SnRealmProvider>();
|
|
||||||
|
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
@ -291,192 +235,31 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(isActive: _isBusy),
|
||||||
if (_channels != null && ResponsiveScaffold.getIsExpand(context))
|
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: MediaQuery.removePadding(
|
||||||
|
context: context,
|
||||||
|
removeTop: true,
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(() => _refreshChannels()),
|
onRefresh: () => Future.wait([
|
||||||
child: Builder(builder: (context) {
|
Future.sync(() => _refreshChannels()),
|
||||||
final scopeList = ListView(
|
_fetchWhatsNew(),
|
||||||
key: const Key('realm-list-view'),
|
]),
|
||||||
padding: EdgeInsets.zero,
|
child: ListView.builder(
|
||||||
children: [
|
itemCount: _channels?.length ?? 0,
|
||||||
ListTile(
|
itemBuilder: (context, idx) {
|
||||||
minTileHeight: 48,
|
final channel = _channels![idx];
|
||||||
leading:
|
final lastMessage = _lastMessages?[channel.id];
|
||||||
const Icon(Symbols.inbox_text).padding(right: 4),
|
|
||||||
contentPadding: EdgeInsets.only(left: 24, right: 24),
|
|
||||||
title: Text('chatDirect').tr(),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (_unreadCountsGrouped?[0] != null &&
|
|
||||||
(_unreadCountsGrouped?[0] ?? 0) > 0)
|
|
||||||
Badge(
|
|
||||||
label: Text(
|
|
||||||
_unreadCountsGrouped![0].toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
setState(() => _isDirect = true);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
...rel.availableRealms.map((ele) {
|
|
||||||
return ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding: EdgeInsets.only(left: 20, right: 24),
|
|
||||||
leading: AccountImage(
|
|
||||||
content: ele.avatar,
|
|
||||||
radius: 16,
|
|
||||||
),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (_unreadCountsGrouped?[ele.id] != null &&
|
|
||||||
(_unreadCountsGrouped?[ele.id] ?? 0) > 0)
|
|
||||||
Badge(
|
|
||||||
label: Text(
|
|
||||||
_unreadCountsGrouped![ele.id].toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
title: Text(ele.name),
|
|
||||||
onTap: () {
|
|
||||||
setState(() => _focusedRealm = ele);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
final directChatList = ListView(
|
return _ChatChannelEntry(
|
||||||
key: Key('direct-chat-list-view'),
|
channel: channel,
|
||||||
padding: EdgeInsets.zero,
|
lastMessage: lastMessage,
|
||||||
children: [
|
unreadCount: _unreadCounts?[channel.id],
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.arrow_left_alt),
|
|
||||||
contentPadding: EdgeInsets.only(left: 24),
|
|
||||||
title: Text('back').tr(),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() => _isDirect = false);
|
_onTapChannel(channel);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
|
||||||
..._channels!.where((ele) => ele.type == 1).map(
|
|
||||||
(ele) {
|
|
||||||
return _ChatChannelEntry(
|
|
||||||
channel: ele,
|
|
||||||
unreadCount: _unreadCounts?[ele.id],
|
|
||||||
lastMessage: _lastMessages?[ele.id],
|
|
||||||
isCompact: true,
|
|
||||||
onTap: () => _onTapChannel(ele),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
final realmScopedChatList = _focusedRealm == null
|
|
||||||
? const SizedBox.shrink()
|
|
||||||
: ListView(
|
|
||||||
key: ValueKey(_focusedRealm),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
children: [
|
|
||||||
if (_focusedRealm!.banner != null)
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
child: AutoResizeUniversalImage(
|
|
||||||
sn.getAttachmentUrl(
|
|
||||||
_focusedRealm!.banner!,
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
tileColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceContainer,
|
|
||||||
leading: AccountImage(
|
|
||||||
content: _focusedRealm!.avatar,
|
|
||||||
radius: 16,
|
|
||||||
),
|
|
||||||
contentPadding: EdgeInsets.only(
|
|
||||||
left: 20,
|
|
||||||
right: 16,
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Symbols.close),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => _focusedRealm = null);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
title: Text(_focusedRealm!.name),
|
|
||||||
),
|
|
||||||
...(_channels!
|
|
||||||
.where(
|
|
||||||
(ele) => ele.realmId == _focusedRealm?.id)
|
|
||||||
.map(
|
|
||||||
(ele) {
|
|
||||||
return _ChatChannelEntry(
|
|
||||||
channel: ele,
|
|
||||||
unreadCount: _unreadCounts?[ele.id],
|
|
||||||
lastMessage: _lastMessages?[ele.id],
|
|
||||||
onTap: () => _onTapChannel(ele),
|
|
||||||
isCompact: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
))
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return PageTransitionSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
transitionBuilder: (Widget child,
|
|
||||||
Animation<double> primaryAnimation,
|
|
||||||
Animation<double> secondaryAnimation) {
|
|
||||||
return SharedAxisTransition(
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
transitionType: SharedAxisTransitionType.horizontal,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: (_focusedRealm == null && !_isDirect)
|
|
||||||
? scopeList
|
|
||||||
: _isDirect
|
|
||||||
? directChatList
|
|
||||||
: realmScopedChatList,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else if (_channels != null)
|
|
||||||
Expanded(
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => Future.sync(() => _refreshChannels()),
|
|
||||||
child: ListView(
|
|
||||||
key: const Key('chat-list-view'),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
children: [
|
|
||||||
...(_channels!.map((ele) {
|
|
||||||
return _ChatChannelEntry(
|
|
||||||
channel: ele,
|
|
||||||
unreadCount: _unreadCounts?[ele.id],
|
|
||||||
lastMessage: _lastMessages?[ele.id],
|
|
||||||
onTap: () => _onTapChannel(ele),
|
|
||||||
);
|
|
||||||
}))
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -491,13 +274,11 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
final int? unreadCount;
|
final int? unreadCount;
|
||||||
final SnChatMessage? lastMessage;
|
final SnChatMessage? lastMessage;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
final bool isCompact;
|
|
||||||
const _ChatChannelEntry({
|
const _ChatChannelEntry({
|
||||||
required this.channel,
|
required this.channel,
|
||||||
this.unreadCount,
|
this.unreadCount,
|
||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.isCompact = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -516,34 +297,6 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
? ud.getFromCache(otherMember.accountId)?.nick ?? channel.name
|
? ud.getFromCache(otherMember.accountId)?.nick ?? channel.name
|
||||||
: channel.name;
|
: channel.name;
|
||||||
|
|
||||||
if (isCompact) {
|
|
||||||
return ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.only(left: otherMember != null ? 20 : 24, right: 24),
|
|
||||||
leading: otherMember != null
|
|
||||||
? AccountImage(
|
|
||||||
content: ud.getFromCache(otherMember.accountId)?.avatar,
|
|
||||||
radius: 16,
|
|
||||||
)
|
|
||||||
: const Icon(Symbols.tag),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (unreadCount != null && (unreadCount ?? 0) > 0)
|
|
||||||
Badge(
|
|
||||||
label: Text(unreadCount.toString()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
title: Text(title),
|
|
||||||
onTap: () {
|
|
||||||
onTap?.call();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -606,7 +359,7 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
content: otherMember != null
|
content: otherMember != null
|
||||||
? ud.getFromCache(otherMember.accountId)?.avatar
|
? ud.getFromCache(otherMember.accountId)?.avatar
|
||||||
: channel.realm?.avatar,
|
: channel.realm?.avatar,
|
||||||
fallbackWidget: const Icon(Symbols.tag, size: 20),
|
fallbackWidget: const Icon(Symbols.chat, size: 20),
|
||||||
),
|
),
|
||||||
onTap: () => onTap?.call(),
|
onTap: () => onTap?.call(),
|
||||||
);
|
);
|
||||||
|
@ -244,7 +244,6 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
GoRouter.of(context).pushNamed('postShuffle');
|
GoRouter.of(context).pushNamed('postShuffle');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
|
|
||||||
const Gap(48),
|
const Gap(48),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
|
@ -325,6 +325,20 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
secondary: const Icon(Symbols.left_panel_close),
|
||||||
|
title: Text('settingsDrawerPreferCollapse').tr(),
|
||||||
|
subtitle:
|
||||||
|
Text('settingsDrawerPreferCollapseDescription').tr(),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false,
|
||||||
|
onChanged: (value) {
|
||||||
|
_prefs.setBool(kAppDrawerPreferCollapse, value ?? false);
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
cfg.calcDrawerSize(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
secondary: const Icon(Symbols.hide),
|
secondary: const Icon(Symbols.hide),
|
||||||
title: Text('settingsHideBottomNav').tr(),
|
title: Text('settingsHideBottomNav').tr(),
|
||||||
|
@ -16,7 +16,12 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
final ws = context.watch<WebSocketProvider>();
|
final ws = context.watch<WebSocketProvider>();
|
||||||
final cfg = context.watch<ConfigProvider>();
|
final cfg = context.watch<ConfigProvider>();
|
||||||
|
|
||||||
final marginLeft = cfg.drawerIsCollapsed ? 0.0 : 80.0;
|
final marginLeft =
|
||||||
|
cfg.drawerIsCollapsed
|
||||||
|
? 0.0
|
||||||
|
: cfg.drawerIsExpanded
|
||||||
|
? 304.0
|
||||||
|
: 80.0;
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: ws,
|
listenable: ws,
|
||||||
@ -30,10 +35,10 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16))),
|
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
child: ua.isAuthorized
|
child:
|
||||||
|
ua.isAuthorized
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -42,30 +47,21 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
if (ws.isBusy)
|
if (ws.isBusy)
|
||||||
Text(
|
Text(
|
||||||
'serverConnecting',
|
'serverConnecting',
|
||||||
).tr().textColor(Theme.of(context)
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer)
|
|
||||||
else if (!ws.isConnected)
|
else if (!ws.isConnected)
|
||||||
Text(
|
Text(
|
||||||
'serverDisconnected',
|
'serverDisconnected',
|
||||||
).tr().textColor(Theme.of(context)
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer)
|
|
||||||
else
|
else
|
||||||
Text(
|
Text(
|
||||||
'serverConnected',
|
'serverConnected',
|
||||||
).tr().textColor(Theme.of(context)
|
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer),
|
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
if (ws.isBusy)
|
if (ws.isBusy)
|
||||||
const CircularProgressIndicator(
|
const CircularProgressIndicator(
|
||||||
strokeWidth: 2.5,
|
strokeWidth: 2.5,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
)
|
).width(12).height(12).padding(horizontal: 4, right: 4)
|
||||||
.width(12)
|
|
||||||
.height(12)
|
|
||||||
.padding(horizontal: 4, right: 4)
|
|
||||||
else if (!ws.isConnected)
|
else if (!ws.isConnected)
|
||||||
const Icon(Symbols.power_off, size: 18)
|
const Icon(Symbols.power_off, size: 18)
|
||||||
else
|
else
|
||||||
@ -73,9 +69,7 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 8, vertical: 4)
|
).padding(horizontal: 8, vertical: 4)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
)
|
).opacity(show ? 1 : 0, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
||||||
.opacity(show ? 1 : 0, animate: true)
|
|
||||||
.animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!ws.isConnected && !ws.isBusy) {
|
if (!ws.isConnected && !ws.isBusy) {
|
||||||
ws.connect();
|
ws.connect();
|
||||||
|
@ -26,7 +26,9 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
final cfg = context.read<ConfigProvider>();
|
final cfg = context.read<ConfigProvider>();
|
||||||
if (!cfg.drawerIsCollapsed) {
|
if (!cfg.drawerIsCollapsed) {
|
||||||
// Leave padding for side navigation
|
// Leave padding for side navigation
|
||||||
mousePosition = mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
mousePosition = cfg.drawerIsExpanded
|
||||||
|
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
|
||||||
|
: mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -38,8 +40,7 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showMenu(BuildContext context, Offset mousePosition) async {
|
void _showMenu(BuildContext context, Offset mousePosition) async {
|
||||||
final menu =
|
final menu = contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
|
||||||
contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
|
|
||||||
final value = await showContextMenu(context, contextMenu: menu);
|
final value = await showContextMenu(context, contextMenu: menu);
|
||||||
onItemSelected?.call(value);
|
onItemSelected?.call(value);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:animations/animations.dart';
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -10,9 +11,14 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/channel.dart';
|
||||||
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/sn_realm.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
import 'package:surface/widgets/version_label.dart';
|
import 'package:surface/widgets/version_label.dart';
|
||||||
|
|
||||||
class AppNavigationDrawer extends StatefulWidget {
|
class AppNavigationDrawer extends StatefulWidget {
|
||||||
@ -39,18 +45,27 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
final nav = context.watch<NavigationProvider>();
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
final cfg = context.watch<ConfigProvider>();
|
||||||
|
|
||||||
|
final backgroundColor = cfg.drawerIsExpanded ? Colors.transparent : null;
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: nav,
|
listenable: nav,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
return Drawer(
|
return Drawer(
|
||||||
elevation: widget.elevation,
|
elevation: widget.elevation,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(0))),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS))
|
(Platform.isWindows ||
|
||||||
|
Platform.isLinux ||
|
||||||
|
Platform.isMacOS) &&
|
||||||
|
!cfg.drawerIsExpanded)
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
@ -63,36 +78,42 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
child: WindowTitleBarBox(),
|
child: WindowTitleBarBox(),
|
||||||
),
|
),
|
||||||
Gap(MediaQuery.of(context).padding.top),
|
Gap(MediaQuery.of(context).padding.top),
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Solar Network').bold(),
|
|
||||||
AppVersionLabel(),
|
|
||||||
],
|
|
||||||
).padding(
|
|
||||||
horizontal: 32,
|
|
||||||
vertical: 12,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: _DrawerContentList(),
|
||||||
padding: EdgeInsets.zero,
|
),
|
||||||
children: [
|
Row(
|
||||||
...nav.destinations.mapIndexed((idx, ele) {
|
spacing: 8,
|
||||||
return ListTile(
|
children:
|
||||||
leading: ele.icon,
|
nav.destinations.where((ele) => ele.isPinned).mapIndexed(
|
||||||
title: Text(ele.label).tr(),
|
(idx, ele) {
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
return Expanded(
|
||||||
selected: nav.currentIndex == idx,
|
child: Tooltip(
|
||||||
onTap: () {
|
message: ele.label.tr(),
|
||||||
GoRouter.of(context).pushNamed(ele.screen);
|
child: IconButton(
|
||||||
|
icon: ele.icon,
|
||||||
|
color: nav.currentIndex == idx
|
||||||
|
? Theme.of(context).colorScheme.onPrimaryContainer
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
|
nav.currentIndex == idx
|
||||||
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer
|
||||||
|
: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).goNamed(ele.screen);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
nav.setIndex(idx);
|
nav.setIndex(idx);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
})
|
},
|
||||||
],
|
).toList(),
|
||||||
),
|
).padding(horizontal: 16, bottom: 8),
|
||||||
),
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -146,3 +167,163 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _DrawerContentList extends StatelessWidget {
|
||||||
|
const _DrawerContentList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ct = context.read<ChatChannelProvider>();
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
final rel = context.watch<SnRealmProvider>();
|
||||||
|
|
||||||
|
return PageTransitionSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (Widget child, Animation<double> primaryAnimation,
|
||||||
|
Animation<double> secondaryAnimation) {
|
||||||
|
return SharedAxisTransition(
|
||||||
|
animation: primaryAnimation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
transitionType: SharedAxisTransitionType.horizontal,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: nav.focusedRealm == null
|
||||||
|
? ListView(
|
||||||
|
key: const Key('realm-list-view'),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
AppVersionLabel(),
|
||||||
|
],
|
||||||
|
).padding(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
...rel.availableRealms.map((ele) {
|
||||||
|
return ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: ele.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
title: Text(ele.name),
|
||||||
|
onTap: () {
|
||||||
|
nav.setFocusedRealm(ele);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding: EdgeInsets.only(left: 28, right: 16),
|
||||||
|
leading: const Icon(Symbols.globe).padding(right: 4),
|
||||||
|
title: Text('screenRealmDiscovery').tr(),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed('realmDiscovery');
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ListView(
|
||||||
|
key: ValueKey(nav.focusedRealm),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
if (nav.focusedRealm!.banner != null)
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(
|
||||||
|
nav.focusedRealm!.banner!,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
tileColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 24,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: nav.focusedRealm!.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () {
|
||||||
|
nav.setFocusedRealm(null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(nav.focusedRealm!.name),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).goNamed(
|
||||||
|
'realmDetail',
|
||||||
|
pathParameters: {
|
||||||
|
'alias': nav.focusedRealm!.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 28,
|
||||||
|
right: 8,
|
||||||
|
),
|
||||||
|
leading: const Icon(Symbols.globe),
|
||||||
|
title: Text('community').tr(),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).goNamed(
|
||||||
|
'realmCommunity',
|
||||||
|
pathParameters: {
|
||||||
|
'alias': nav.focusedRealm!.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (ct.availableChannels
|
||||||
|
.where((ele) => ele.realmId == nav.focusedRealm?.id)
|
||||||
|
.isNotEmpty)
|
||||||
|
const Divider(height: 1),
|
||||||
|
...(ct.availableChannels
|
||||||
|
.where((ele) => ele.realmId == nav.focusedRealm?.id)
|
||||||
|
.map((ele) {
|
||||||
|
return ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 28,
|
||||||
|
right: 8,
|
||||||
|
),
|
||||||
|
leading: const Icon(Symbols.tag),
|
||||||
|
title: Text(ele.name),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).goNamed(
|
||||||
|
'chatRoom',
|
||||||
|
pathParameters: {
|
||||||
|
'scope': ele.realm?.alias ?? 'global',
|
||||||
|
'alias': ele.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,6 @@ import 'package:surface/providers/navigation.dart';
|
|||||||
import 'package:surface/widgets/connection_indicator.dart';
|
import 'package:surface/widgets/connection_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_background.dart';
|
import 'package:surface/widgets/navigation/app_background.dart';
|
||||||
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
||||||
import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
|
|
||||||
import 'package:surface/widgets/navigation/app_rail_navigation.dart';
|
import 'package:surface/widgets/navigation/app_rail_navigation.dart';
|
||||||
import 'package:surface/widgets/notify_indicator.dart';
|
import 'package:surface/widgets/notify_indicator.dart';
|
||||||
|
|
||||||
@ -222,7 +221,6 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawerEdgeDragWidth: isPopable ? 0 : null,
|
drawerEdgeDragWidth: isPopable ? 0 : null,
|
||||||
drawer: isCollapseDrawer ? const AppNavigationDrawer() : null,
|
|
||||||
bottomNavigationBar:
|
bottomNavigationBar:
|
||||||
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user