Compare commits
4 Commits
a1c4e5eca0
...
908f0cb59e
Author | SHA1 | Date | |
---|---|---|---|
908f0cb59e | |||
7c2b8de931 | |||
6bb9c21759 | |||
8f2fc55608 |
BIN
assets/audio/sfx/launch-done.mp3
Normal file
BIN
assets/audio/sfx/launch-done.mp3
Normal file
Binary file not shown.
@ -939,5 +939,7 @@
|
|||||||
"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,5 +936,7 @@
|
|||||||
"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-intro-player');
|
final player = AudioPlayer(playerId: 'launch-done-player');
|
||||||
await player.play(AssetSource('audio/sfx/launch-intro.mp3'), volume: 0.5);
|
await player.play(AssetSource('audio/sfx/launch-done.mp3'), volume: 0.8);
|
||||||
player.onPlayerComplete.listen((_) {
|
player.onPlayerComplete.listen((_) {
|
||||||
player.dispose();
|
player.dispose();
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,6 @@ 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';
|
||||||
@ -47,27 +46,17 @@ 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 (newDrawerIsExpanded != drawerIsExpanded ||
|
if (newDrawerIsCollapsed != drawerIsCollapsed) {
|
||||||
newDrawerIsCollapsed != drawerIsCollapsed) {
|
|
||||||
drawerIsExpanded = newDrawerIsExpanded;
|
|
||||||
drawerIsCollapsed = newDrawerIsCollapsed;
|
drawerIsCollapsed = newDrawerIsCollapsed;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ 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;
|
||||||
@ -135,11 +134,4 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
_currentIndex = idx;
|
_currentIndex = idx;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
SnRealm? focusedRealm;
|
|
||||||
|
|
||||||
void setFocusedRealm(SnRealm? realm) {
|
|
||||||
focusedRealm = realm;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,7 @@ 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,3 +1,5 @@
|
|||||||
|
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';
|
||||||
@ -6,11 +8,14 @@ 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';
|
||||||
@ -18,6 +23,7 @@ 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 {
|
||||||
@ -35,6 +41,7 @@ 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>();
|
||||||
@ -42,19 +49,48 @@ 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 = {for (var v in out) v['channel_id']: v['count']};
|
_unreadCounts ??= {};
|
||||||
|
_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 noRemote = false}) {
|
void _refreshChannels({bool withBoost = false, 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: noRemote).listen((channels) async {
|
chan.fetchChannels(noRemote: true).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) {
|
||||||
@ -96,6 +132,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
..onDone(() {
|
..onDone(() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
|
_fetchWhatsNew();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,29 +167,48 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshChannels();
|
_refreshChannels(withBoost: true);
|
||||||
_fetchWhatsNew();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapChannel(SnChannel channel) {
|
void _onTapChannel(SnChannel channel) {
|
||||||
setState(() => _unreadCounts?[channel.id] = 0);
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
GoRouter.of(context).pushReplacementNamed(
|
if (ResponsiveScaffold.getIsExpand(context)) {
|
||||||
'chatRoom',
|
GoRouter.of(context).pushReplacementNamed(
|
||||||
pathParameters: {
|
'chatRoom',
|
||||||
'scope': channel.realm?.alias ?? 'global',
|
pathParameters: {
|
||||||
'alias': channel.alias,
|
'scope': channel.realm?.alias ?? 'global',
|
||||||
},
|
'alias': channel.alias,
|
||||||
).then((value) {
|
},
|
||||||
if (mounted) {
|
).then((value) {
|
||||||
setState(() => _unreadCounts?[channel.id] = 0);
|
if (mounted) {
|
||||||
_refreshChannels(noRemote: true);
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
}
|
_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(
|
||||||
@ -235,34 +291,195 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(isActive: _isBusy),
|
||||||
Expanded(
|
if (_channels != null && ResponsiveScaffold.getIsExpand(context))
|
||||||
child: MediaQuery.removePadding(
|
Expanded(
|
||||||
context: context,
|
|
||||||
removeTop: true,
|
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.wait([
|
onRefresh: () => Future.sync(() => _refreshChannels()),
|
||||||
Future.sync(() => _refreshChannels()),
|
child: Builder(builder: (context) {
|
||||||
_fetchWhatsNew(),
|
final scopeList = ListView(
|
||||||
]),
|
key: const Key('realm-list-view'),
|
||||||
child: ListView.builder(
|
padding: EdgeInsets.zero,
|
||||||
itemCount: _channels?.length ?? 0,
|
children: [
|
||||||
itemBuilder: (context, idx) {
|
ListTile(
|
||||||
final channel = _channels![idx];
|
minTileHeight: 48,
|
||||||
final lastMessage = _lastMessages?[channel.id];
|
leading:
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
return _ChatChannelEntry(
|
final directChatList = ListView(
|
||||||
channel: channel,
|
key: Key('direct-chat-list-view'),
|
||||||
lastMessage: lastMessage,
|
padding: EdgeInsets.zero,
|
||||||
unreadCount: _unreadCounts?[channel.id],
|
children: [
|
||||||
onTap: () {
|
ListTile(
|
||||||
_onTapChannel(channel);
|
minTileHeight: 48,
|
||||||
},
|
leading: const Icon(Symbols.arrow_left_alt),
|
||||||
);
|
contentPadding: EdgeInsets.only(left: 24),
|
||||||
},
|
title: Text('back').tr(),
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _isDirect = false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -274,11 +491,13 @@ 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
|
||||||
@ -297,6 +516,34 @@ 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: [
|
||||||
@ -359,7 +606,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.chat, size: 20),
|
fallbackWidget: const Icon(Symbols.tag, size: 20),
|
||||||
),
|
),
|
||||||
onTap: () => onTap?.call(),
|
onTap: () => onTap?.call(),
|
||||||
);
|
);
|
||||||
|
@ -244,7 +244,8 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
GoRouter.of(context).pushNamed('postShuffle');
|
GoRouter.of(context).pushNamed('postShuffle');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Gap(48),
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
|
||||||
|
const Gap(48),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
@ -325,20 +325,6 @@ 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,12 +16,7 @@ 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 =
|
final marginLeft = cfg.drawerIsCollapsed ? 0.0 : 80.0;
|
||||||
cfg.drawerIsCollapsed
|
|
||||||
? 0.0
|
|
||||||
: cfg.drawerIsExpanded
|
|
||||||
? 304.0
|
|
||||||
: 80.0;
|
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: ws,
|
listenable: ws,
|
||||||
@ -35,41 +30,52 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
child:
|
child: ua.isAuthorized
|
||||||
ua.isAuthorized
|
? Row(
|
||||||
? Row(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
||||||
children: [
|
if (ws.isBusy)
|
||||||
if (ws.isBusy)
|
Text(
|
||||||
Text(
|
'serverConnecting',
|
||||||
'serverConnecting',
|
).tr().textColor(Theme.of(context)
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
.colorScheme
|
||||||
else if (!ws.isConnected)
|
.onSecondaryContainer)
|
||||||
Text(
|
else if (!ws.isConnected)
|
||||||
'serverDisconnected',
|
Text(
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
'serverDisconnected',
|
||||||
else
|
).tr().textColor(Theme.of(context)
|
||||||
Text(
|
.colorScheme
|
||||||
'serverConnected',
|
.onSecondaryContainer)
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
|
else
|
||||||
const Gap(8),
|
Text(
|
||||||
if (ws.isBusy)
|
'serverConnected',
|
||||||
const CircularProgressIndicator(
|
).tr().textColor(Theme.of(context)
|
||||||
strokeWidth: 2.5,
|
.colorScheme
|
||||||
padding: EdgeInsets.zero,
|
.onSecondaryContainer),
|
||||||
).width(12).height(12).padding(horizontal: 4, right: 4)
|
const Gap(8),
|
||||||
else if (!ws.isConnected)
|
if (ws.isBusy)
|
||||||
const Icon(Symbols.power_off, size: 18)
|
const CircularProgressIndicator(
|
||||||
else
|
strokeWidth: 2.5,
|
||||||
const Icon(Symbols.power, size: 18),
|
padding: EdgeInsets.zero,
|
||||||
],
|
)
|
||||||
).padding(horizontal: 8, vertical: 4)
|
.width(12)
|
||||||
: const SizedBox.shrink(),
|
.height(12)
|
||||||
).opacity(show ? 1 : 0, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
.padding(horizontal: 4, right: 4)
|
||||||
|
else if (!ws.isConnected)
|
||||||
|
const Icon(Symbols.power_off, size: 18)
|
||||||
|
else
|
||||||
|
const Icon(Symbols.power, size: 18),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 8, vertical: 4)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
)
|
||||||
|
.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,9 +26,7 @@ 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 = cfg.drawerIsExpanded
|
mousePosition = mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
||||||
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
|
|
||||||
: mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -40,7 +38,8 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showMenu(BuildContext context, Offset mousePosition) async {
|
void _showMenu(BuildContext context, Offset mousePosition) async {
|
||||||
final menu = contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
|
final menu =
|
||||||
|
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,6 +1,5 @@
|
|||||||
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';
|
||||||
@ -11,14 +10,9 @@ 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 {
|
||||||
@ -45,27 +39,18 @@ 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.isWindows || Platform.isLinux || Platform.isMacOS))
|
||||||
Platform.isLinux ||
|
|
||||||
Platform.isMacOS) &&
|
|
||||||
!cfg.drawerIsExpanded)
|
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
@ -78,42 +63,36 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
child: WindowTitleBarBox(),
|
child: WindowTitleBarBox(),
|
||||||
),
|
),
|
||||||
Gap(MediaQuery.of(context).padding.top),
|
Gap(MediaQuery.of(context).padding.top),
|
||||||
Expanded(
|
Column(
|
||||||
child: _DrawerContentList(),
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
AppVersionLabel(),
|
||||||
|
],
|
||||||
|
).padding(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
...nav.destinations.mapIndexed((idx, ele) {
|
||||||
|
return ListTile(
|
||||||
|
leading: ele.icon,
|
||||||
|
title: Text(ele.label).tr(),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
selected: nav.currentIndex == idx,
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(ele.screen);
|
||||||
|
nav.setIndex(idx);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Row(
|
|
||||||
spacing: 8,
|
|
||||||
children:
|
|
||||||
nav.destinations.where((ele) => ele.isPinned).mapIndexed(
|
|
||||||
(idx, ele) {
|
|
||||||
return Expanded(
|
|
||||||
child: Tooltip(
|
|
||||||
message: ele.label.tr(),
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).toList(),
|
|
||||||
).padding(horizontal: 16, bottom: 8),
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -167,163 +146,3 @@ 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,6 +13,7 @@ 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';
|
||||||
|
|
||||||
@ -221,6 +222,7 @@ 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