User cache

This commit is contained in:
2025-03-04 22:30:17 +08:00
parent 1478933cf1
commit 288c0399f9
13 changed files with 422 additions and 167 deletions

View File

@ -336,7 +336,7 @@ class _ChatChannelEntry extends StatelessWidget {
: null;
final title = otherMember != null
? ud.getAccountFromCache(otherMember.accountId)?.nick ?? channel.name
? ud.getFromCache(otherMember.accountId)?.nick ?? channel.name
: channel.name;
return ListTile(
@ -354,10 +354,9 @@ class _ChatChannelEntry extends StatelessWidget {
? Row(
children: [
Badge(
label: Text(ud
.getAccountFromCache(lastMessage!.sender.accountId)
?.nick ??
'unknown'.tr()),
label: Text(
ud.getFromCache(lastMessage!.sender.accountId)?.nick ??
'unknown'.tr()),
backgroundColor: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
@ -400,7 +399,7 @@ class _ChatChannelEntry extends StatelessWidget {
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage(
content: otherMember != null
? ud.getAccountFromCache(otherMember.accountId)?.avatar
? ud.getFromCache(otherMember.accountId)?.avatar
: channel.realm?.avatar,
fallbackWidget: const Icon(Symbols.chat, size: 20),
),

View File

@ -289,15 +289,14 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
),
ListTile(
leading: AccountImage(
content:
ud.getAccountFromCache(_profile!.accountId)?.avatar,
content: ud.getFromCache(_profile!.accountId)?.avatar,
radius: 18,
),
trailing: const Icon(Symbols.chevron_right),
title: Text('channelEditProfile').tr(),
subtitle: Text(
(_profile?.nick?.isEmpty ?? true)
? ud.getAccountFromCache(_profile!.accountId)!.nick
? ud.getFromCache(_profile!.accountId)!.nick
: _profile!.nick!,
),
contentPadding: const EdgeInsets.only(left: 20, right: 20),
@ -575,11 +574,10 @@ class _ChannelMemberListWidgetState extends State<_ChannelMemberListWidget> {
return ListTile(
contentPadding: const EdgeInsets.only(right: 24, left: 16),
leading: AccountImage(
content: ud.getAccountFromCache(member.accountId)?.avatar,
content: ud.getFromCache(member.accountId)?.avatar,
),
title: Text(
ud.getAccountFromCache(member.accountId)?.name ??
'unknown'.tr(),
ud.getFromCache(member.accountId)?.name ?? 'unknown'.tr(),
),
subtitle: Text(member.nick ?? 'unknown'.tr()),
trailing: SizedBox(

View File

@ -277,8 +277,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
appBar: AppBar(
title: Text(
_channel?.type == 1
? ud.getAccountFromCache(_otherMember?.accountId)?.nick ??
_channel!.name
? ud.getFromCache(_otherMember?.accountId)?.nick ?? _channel!.name
: _channel?.name ?? 'loading'.tr(),
),
actions: [

View File

@ -51,7 +51,8 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
Future<void> _fetchPublishers() async {
try {
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/co/publishers?realm=${widget.alias}');
final resp =
await sn.client.get('/cgi/co/publishers?realm=${widget.alias}');
_publishers = List<SnPublisher>.from(
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
);
@ -68,7 +69,8 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
Future<void> _fetchChannels() async {
try {
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/im/channels/${widget.alias}/public');
final resp =
await sn.client.get('/cgi/im/channels/${widget.alias}/public');
_channels = List<SnChannel>.from(
resp.data.map((e) => SnChannel.fromJson(e)).cast<SnChannel>(),
);
@ -98,15 +100,32 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
title: Text(_realm?.name ?? 'loading'.tr()),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Symbols.home, color: Theme.of(context).appBarTheme.foregroundColor)),
Tab(icon: Icon(Symbols.explore, color: Theme.of(context).appBarTheme.foregroundColor)),
Tab(icon: Icon(Symbols.group, color: Theme.of(context).appBarTheme.foregroundColor)),
Tab(icon: Icon(Symbols.settings, color: Theme.of(context).appBarTheme.foregroundColor)),
Tab(
icon: Icon(Symbols.home,
color: Theme.of(context)
.appBarTheme
.foregroundColor)),
Tab(
icon: Icon(Symbols.explore,
color: Theme.of(context)
.appBarTheme
.foregroundColor)),
Tab(
icon: Icon(Symbols.group,
color: Theme.of(context)
.appBarTheme
.foregroundColor)),
Tab(
icon: Icon(Symbols.settings,
color: Theme.of(context)
.appBarTheme
.foregroundColor)),
],
),
),
@ -115,7 +134,8 @@ class _RealmDetailScreenState extends State<RealmDetailScreen> {
},
body: TabBarView(
children: [
_RealmDetailHomeWidget(realm: _realm, publishers: _publishers, channels: _channels),
_RealmDetailHomeWidget(
realm: _realm, publishers: _publishers, channels: _channels),
_RealmPostListWidget(realm: _realm),
_RealmMemberListWidget(realm: _realm),
_RealmSettingsWidget(
@ -137,7 +157,8 @@ class _RealmDetailHomeWidget extends StatelessWidget {
final List<SnPublisher>? publishers;
final List<SnChannel>? channels;
const _RealmDetailHomeWidget({required this.realm, this.publishers, this.channels});
const _RealmDetailHomeWidget(
{required this.realm, this.publishers, this.channels});
@override
Widget build(BuildContext context) {
@ -168,7 +189,8 @@ class _RealmDetailHomeWidget extends StatelessWidget {
child: Container(
width: double.infinity,
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child: Text('realmCommunityPublishersHint'.tr(), style: Theme.of(context).textTheme.bodyMedium)
child: Text('realmCommunityPublishersHint'.tr(),
style: Theme.of(context).textTheme.bodyMedium)
.padding(horizontal: 24, vertical: 8),
),
),
@ -199,7 +221,8 @@ class _RealmDetailHomeWidget extends StatelessWidget {
child: Container(
width: double.infinity,
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child: Text('realmCommunityPublicChannelsHint'.tr(), style: Theme.of(context).textTheme.bodyMedium)
child: Text('realmCommunityPublicChannelsHint'.tr(),
style: Theme.of(context).textTheme.bodyMedium)
.padding(horizontal: 24, vertical: 8),
),
),
@ -323,10 +346,12 @@ class _RealmMemberListWidgetState extends State<_RealmMemberListWidget> {
try {
final ud = context.read<UserDirectoryProvider>();
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/id/realms/${widget.realm!.alias}/members', queryParameters: {
'take': 10,
'offset': _members.length,
});
final resp = await sn.client.get(
'/cgi/id/realms/${widget.realm!.alias}/members',
queryParameters: {
'take': 10,
'offset': _members.length,
});
final out = List<SnRealmMember>.from(
resp.data['data']?.map((e) => SnRealmMember.fromJson(e)) ?? [],
@ -432,14 +457,14 @@ class _RealmMemberListWidgetState extends State<_RealmMemberListWidget> {
return ListTile(
contentPadding: const EdgeInsets.only(right: 24, left: 16),
leading: AccountImage(
content: ud.getAccountFromCache(member.accountId)?.avatar,
content: ud.getFromCache(member.accountId)?.avatar,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
title: Text(
ud.getAccountFromCache(member.accountId)?.nick ?? 'unknown'.tr(),
ud.getFromCache(member.accountId)?.nick ?? 'unknown'.tr(),
),
subtitle: Text(
ud.getAccountFromCache(member.accountId)?.name ?? 'unknown'.tr(),
ud.getFromCache(member.accountId)?.name ?? 'unknown'.tr(),
),
trailing: IconButton(
icon: const Icon(Symbols.person_remove),

View File

@ -51,8 +51,10 @@ class _AppSharingListenerState extends State<AppSharingListener> {
child: Column(
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
contentPadding:
const EdgeInsets.symmetric(horizontal: 24),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
leading: Icon(Icons.post_add),
trailing: const Icon(Icons.chevron_right),
title: Text('shareIntentPostStory').tr(),
@ -64,13 +66,20 @@ class _AppSharingListenerState extends State<AppSharingListener> {
},
extra: PostEditorExtra(
text: value
.where((e) => [SharedMediaType.text, SharedMediaType.url].contains(e.type))
.where((e) => [
SharedMediaType.text,
SharedMediaType.url
].contains(e.type))
.map((e) => e.path)
.join('\n'),
attachments: value
.where((e) => [SharedMediaType.video, SharedMediaType.file, SharedMediaType.image]
.contains(e.type))
.map((e) => PostWriteMedia.fromFile(XFile(e.path)))
.where((e) => [
SharedMediaType.video,
SharedMediaType.file,
SharedMediaType.image
].contains(e.type))
.map((e) =>
PostWriteMedia.fromFile(XFile(e.path)))
.toList(),
),
);
@ -78,15 +87,18 @@ class _AppSharingListenerState extends State<AppSharingListener> {
},
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
contentPadding:
const EdgeInsets.symmetric(horizontal: 24),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
leading: Icon(Icons.chat_outlined),
trailing: const Icon(Icons.chevron_right),
title: Text('shareIntentSendChannel').tr(),
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => _ShareIntentChannelSelect(value: value),
builder: (context) =>
_ShareIntentChannelSelect(value: value),
).then((val) {
if (!context.mounted) return;
if (val == true) Navigator.pop(context);
@ -110,7 +122,8 @@ class _AppSharingListenerState extends State<AppSharingListener> {
}
void _initialize() async {
_shareIntentSubscription = ReceiveSharingIntent.instance.getMediaStream().listen((value) {
_shareIntentSubscription =
ReceiveSharingIntent.instance.getMediaStream().listen((value) {
if (value.isEmpty) return;
if (mounted) {
_gotoPost(value);
@ -157,7 +170,8 @@ class _ShareIntentChannelSelect extends StatefulWidget {
const _ShareIntentChannelSelect({required this.value});
@override
State<_ShareIntentChannelSelect> createState() => _ShareIntentChannelSelectState();
State<_ShareIntentChannelSelect> createState() =>
_ShareIntentChannelSelectState();
}
class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
@ -178,8 +192,11 @@ class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
final lastMessages = await chan.getLastMessages(channels);
_lastMessages = {for (final val in lastMessages) val.channelId: val};
channels.sort((a, b) {
if (_lastMessages!.containsKey(a.id) && _lastMessages!.containsKey(b.id)) {
return _lastMessages![b.id]!.createdAt.compareTo(_lastMessages![a.id]!.createdAt);
if (_lastMessages!.containsKey(a.id) &&
_lastMessages!.containsKey(b.id)) {
return _lastMessages![b.id]!
.createdAt
.compareTo(_lastMessages![a.id]!.createdAt);
}
if (_lastMessages!.containsKey(a.id)) return -1;
if (_lastMessages!.containsKey(b.id)) return 1;
@ -232,7 +249,9 @@ class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
children: [
const Icon(Symbols.chat, size: 24),
const Gap(16),
Text('shareIntentSendChannel', style: Theme.of(context).textTheme.titleLarge).tr(),
Text('shareIntentSendChannel',
style: Theme.of(context).textTheme.titleLarge)
.tr(),
],
).padding(horizontal: 20, top: 16, bottom: 12),
LoadingIndicator(isActive: _isBusy),
@ -249,29 +268,34 @@ class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
final lastMessage = _lastMessages?[channel.id];
if (channel.type == 1) {
final otherMember = channel.members?.cast<SnChannelMember?>().firstWhere(
(ele) => ele?.accountId != ua.user?.id,
orElse: () => null,
);
final otherMember =
channel.members?.cast<SnChannelMember?>().firstWhere(
(ele) => ele?.accountId != ua.user?.id,
orElse: () => null,
);
return ListTile(
title: Text(ud.getAccountFromCache(otherMember?.accountId)?.nick ?? channel.name),
title: Text(
ud.getFromCache(otherMember?.accountId)?.nick ??
channel.name),
subtitle: lastMessage != null
? Text(
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
'${ud.getFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: Text(
'channelDirectMessageDescription'.tr(args: [
'@${ud.getAccountFromCache(otherMember?.accountId)?.name}',
'@${ud.getFromCache(otherMember?.accountId)?.name}',
]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage(
content: ud.getAccountFromCache(otherMember?.accountId)?.avatar,
content:
ud.getFromCache(otherMember?.accountId)?.avatar,
),
onTap: () {
GoRouter.of(context).pushNamed(
@ -291,7 +315,7 @@ class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
title: Text(channel.name),
subtitle: lastMessage != null
? Text(
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
'${ud.getFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
@ -316,13 +340,20 @@ class _ShareIntentChannelSelectState extends State<_ShareIntentChannelSelect> {
},
extra: ChatRoomScreenExtra(
initialText: widget.value
.where((e) => [SharedMediaType.text, SharedMediaType.url].contains(e.type))
.where((e) => [
SharedMediaType.text,
SharedMediaType.url
].contains(e.type))
.map((e) => e.path)
.join('\n'),
initialAttachments: widget.value
.where((e) =>
[SharedMediaType.video, SharedMediaType.file, SharedMediaType.image].contains(e.type))
.map((e) => PostWriteMedia.fromFile(XFile(e.path)))
.where((e) => [
SharedMediaType.video,
SharedMediaType.file,
SharedMediaType.image
].contains(e.type))
.map(
(e) => PostWriteMedia.fromFile(XFile(e.path)))
.toList(),
),
)