✨ Desktop chat list
🍱 Update launch sfx
This commit is contained in:
parent
6bb9c21759
commit
7c2b8de931
assets
lib
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();
|
||||||
});
|
});
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
@ -130,29 +166,49 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshChannels();
|
_refreshChannels(withBoost: true);
|
||||||
_fetchWhatsNew();
|
_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,178 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(isActive: _isBusy),
|
||||||
Expanded(
|
if (_channels != null)
|
||||||
child: MediaQuery.removePadding(
|
Expanded(
|
||||||
context: context,
|
|
||||||
removeTop: true,
|
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.wait([
|
onRefresh: () => Future.wait([
|
||||||
Future.sync(() => _refreshChannels()),
|
Future.sync(() => _refreshChannels()),
|
||||||
_fetchWhatsNew(),
|
_fetchWhatsNew(),
|
||||||
]),
|
]),
|
||||||
child: ListView.builder(
|
child: Builder(builder: (context) {
|
||||||
itemCount: _channels?.length ?? 0,
|
final scopeList = ListView(
|
||||||
itemBuilder: (context, idx) {
|
key: const Key('realm-list-view'),
|
||||||
final channel = _channels![idx];
|
padding: EdgeInsets.zero,
|
||||||
final lastMessage = _lastMessages?[channel.id];
|
children: [
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -274,11 +474,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 +499,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 +589,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(),
|
||||||
);
|
);
|
||||||
|
@ -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,13 +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/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 {
|
||||||
@ -151,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();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user