mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-23 20:49:26 +01:00
refactor: Switch to Hive Collections DB
This commit is contained in:
parent
643b5c84eb
commit
c2df8f4b4b
@ -461,11 +461,11 @@ class ChatController extends State<Chat> {
|
|||||||
if (selectedEvents.length == 1) {
|
if (selectedEvents.length == 1) {
|
||||||
return selectedEvents.first
|
return selectedEvents.first
|
||||||
.getDisplayEvent(timeline!)
|
.getDisplayEvent(timeline!)
|
||||||
.getLocalizedBody(MatrixLocals(L10n.of(context)!));
|
.calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!));
|
||||||
}
|
}
|
||||||
for (final event in selectedEvents) {
|
for (final event in selectedEvents) {
|
||||||
if (copyString.isNotEmpty) copyString += '\n\n';
|
if (copyString.isNotEmpty) copyString += '\n\n';
|
||||||
copyString += event.getDisplayEvent(timeline!).getLocalizedBody(
|
copyString += event.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: true);
|
withSenderNamePrefix: true);
|
||||||
}
|
}
|
||||||
@ -773,7 +773,7 @@ class ChatController extends State<Chat> {
|
|||||||
editEvent = selectedEvents.first;
|
editEvent = selectedEvents.first;
|
||||||
inputText = sendController.text = editEvent!
|
inputText = sendController.text = editEvent!
|
||||||
.getDisplayEvent(timeline!)
|
.getDisplayEvent(timeline!)
|
||||||
.getLocalizedBody(MatrixLocals(L10n.of(context)!),
|
.calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false, hideReply: true);
|
withSenderNamePrefix: false, hideReply: true);
|
||||||
selectedEvents.clear();
|
selectedEvents.clear();
|
||||||
});
|
});
|
||||||
|
@ -29,10 +29,11 @@ class ChatAppBarTitle extends StatelessWidget {
|
|||||||
? () => showModalBottomSheet(
|
? () => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => UserBottomSheet(
|
builder: (c) => UserBottomSheet(
|
||||||
user: room.getUserByMXIDSync(directChatMatrixID),
|
user: room
|
||||||
|
.unsafeGetUserFromMemoryOrFallback(directChatMatrixID),
|
||||||
outerContext: context,
|
outerContext: context,
|
||||||
onMention: () => controller.sendController.text +=
|
onMention: () => controller.sendController.text +=
|
||||||
'${room.getUserByMXIDSync(directChatMatrixID).mention} ',
|
'${room.unsafeGetUserFromMemoryOrFallback(directChatMatrixID).mention} ',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: () => VRouter.of(context).toSegments(['rooms', room.id, 'details']),
|
: () => VRouter.of(context).toSegments(['rooms', room.id, 'details']),
|
||||||
|
@ -293,14 +293,14 @@ class _ChatAccountPicker extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: controller.sendingClient!.ownProfile,
|
future: controller.sendingClient!.fetchOwnProfile(),
|
||||||
builder: (context, snapshot) => PopupMenuButton<String>(
|
builder: (context, snapshot) => PopupMenuButton<String>(
|
||||||
onSelected: _popupMenuButtonSelected,
|
onSelected: _popupMenuButtonSelected,
|
||||||
itemBuilder: (BuildContext context) => clients
|
itemBuilder: (BuildContext context) => clients
|
||||||
.map((client) => PopupMenuItem<String>(
|
.map((client) => PopupMenuItem<String>(
|
||||||
value: client!.userID,
|
value: client!.userID,
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: client.ownProfile,
|
future: client.fetchOwnProfile(),
|
||||||
builder: (context, snapshot) => ListTile(
|
builder: (context, snapshot) => ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
mxContent: snapshot.data?.avatarUrl,
|
mxContent: snapshot.data?.avatarUrl,
|
||||||
|
@ -350,12 +350,12 @@ class ChatView extends StatelessWidget {
|
|||||||
builder: (c) =>
|
builder: (c) =>
|
||||||
UserBottomSheet(
|
UserBottomSheet(
|
||||||
user: event
|
user: event
|
||||||
.sender,
|
.senderFromMemoryOrFallback,
|
||||||
outerContext:
|
outerContext:
|
||||||
context,
|
context,
|
||||||
onMention: () => controller
|
onMention: () => controller
|
||||||
.sendController
|
.sendController
|
||||||
.text += '${event.sender.mention} ',
|
.text += '${event.senderFromMemoryOrFallback.mention} ',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
unfold: controller
|
unfold: controller
|
||||||
|
@ -48,12 +48,12 @@ class EventInfoDialog extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
mxContent: event.sender.avatarUrl,
|
mxContent: event.senderFromMemoryOrFallback.avatarUrl,
|
||||||
name: event.sender.calcDisplayname(),
|
name: event.senderFromMemoryOrFallback.calcDisplayname(),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context)!.sender),
|
title: Text(L10n.of(context)!.sender),
|
||||||
subtitle:
|
subtitle: Text(
|
||||||
Text('${event.sender.calcDisplayname()} [${event.senderId}]'),
|
'${event.senderFromMemoryOrFallback.calcDisplayname()} [${event.senderId}]'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context)!.time),
|
title: Text(L10n.of(context)!.time),
|
||||||
|
@ -75,7 +75,7 @@ class Message extends StatelessWidget {
|
|||||||
EventTypes.Sticker,
|
EventTypes.Sticker,
|
||||||
EventTypes.Encrypted,
|
EventTypes.Encrypted,
|
||||||
].contains(nextEvent!.type)
|
].contains(nextEvent!.type)
|
||||||
? nextEvent!.sender.id == event.sender.id && !displayTime
|
? nextEvent!.senderId == event.senderId && !displayTime
|
||||||
: false;
|
: false;
|
||||||
final textColor = ownMessage
|
final textColor = ownMessage
|
||||||
? Theme.of(context).colorScheme.onPrimary
|
? Theme.of(context).colorScheme.onPrimary
|
||||||
@ -125,11 +125,16 @@ class Message extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
: Avatar(
|
: FutureBuilder<User?>(
|
||||||
mxContent: event.sender.avatarUrl,
|
future: event.fetchSenderUser(),
|
||||||
name: event.sender.calcDisplayname(),
|
builder: (context, snapshot) {
|
||||||
onTap: () => onAvatarTab!(event),
|
final user = snapshot.data ?? event.senderFromMemoryOrFallback;
|
||||||
),
|
return Avatar(
|
||||||
|
mxContent: user.avatarUrl,
|
||||||
|
name: user.calcDisplayname(),
|
||||||
|
onTap: () => onAvatarTab!(event),
|
||||||
|
);
|
||||||
|
}),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -140,14 +145,22 @@ class Message extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(left: 8.0, bottom: 4),
|
padding: const EdgeInsets.only(left: 8.0, bottom: 4),
|
||||||
child: ownMessage || event.room.isDirectChat
|
child: ownMessage || event.room.isDirectChat
|
||||||
? const SizedBox(height: 12)
|
? const SizedBox(height: 12)
|
||||||
: Text(
|
: FutureBuilder<User?>(
|
||||||
event.sender.calcDisplayname(),
|
future: event.fetchSenderUser(),
|
||||||
style: TextStyle(
|
builder: (context, snapshot) {
|
||||||
fontSize: 12,
|
final displayname =
|
||||||
fontWeight: FontWeight.bold,
|
snapshot.data?.calcDisplayname() ??
|
||||||
color: event.sender.calcDisplayname().color,
|
event.senderFromMemoryOrFallback
|
||||||
),
|
.calcDisplayname();
|
||||||
),
|
return Text(
|
||||||
|
displayname,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: displayname.color,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
|
@ -34,7 +34,7 @@ class MessageContent extends StatelessWidget {
|
|||||||
content: Text(
|
content: Text(
|
||||||
event.type == EventTypes.Encrypted
|
event.type == EventTypes.Encrypted
|
||||||
? L10n.of(context)!.needPantalaimonWarning
|
? L10n.of(context)!.needPantalaimonWarning
|
||||||
: event.getLocalizedBody(
|
: event.calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
@ -172,48 +172,73 @@ class MessageContent extends StatelessWidget {
|
|||||||
textmessage:
|
textmessage:
|
||||||
default:
|
default:
|
||||||
if (event.redacted) {
|
if (event.redacted) {
|
||||||
return _ButtonContent(
|
return FutureBuilder<User?>(
|
||||||
label: L10n.of(context)!
|
future: event.fetchSenderUser(),
|
||||||
.redactedAnEvent(event.sender.calcDisplayname()),
|
builder: (context, snapshot) {
|
||||||
icon: const Icon(Icons.delete_outlined),
|
return _ButtonContent(
|
||||||
textColor: buttonTextColor,
|
label: L10n.of(context)!.redactedAnEvent(snapshot.data
|
||||||
onPressed: () => onInfoTab!(event),
|
?.calcDisplayname() ??
|
||||||
);
|
event.senderFromMemoryOrFallback.calcDisplayname()),
|
||||||
|
icon: const Icon(Icons.delete_outlined),
|
||||||
|
textColor: buttonTextColor,
|
||||||
|
onPressed: () => onInfoTab!(event),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
final bigEmotes = event.onlyEmotes &&
|
final bigEmotes = event.onlyEmotes &&
|
||||||
event.numberEmotes > 0 &&
|
event.numberEmotes > 0 &&
|
||||||
event.numberEmotes <= 10;
|
event.numberEmotes <= 10;
|
||||||
return LinkText(
|
return FutureBuilder<String>(
|
||||||
text: event.getLocalizedBody(MatrixLocals(L10n.of(context)!),
|
future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!),
|
||||||
hideReply: true),
|
hideReply: true),
|
||||||
textStyle: TextStyle(
|
builder: (context, snapshot) {
|
||||||
color: textColor,
|
return LinkText(
|
||||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
text: snapshot.data ??
|
||||||
decoration: event.redacted ? TextDecoration.lineThrough : null,
|
event.calcLocalizedBodyFallback(
|
||||||
),
|
MatrixLocals(L10n.of(context)!),
|
||||||
linkStyle: TextStyle(
|
hideReply: true),
|
||||||
color: textColor.withAlpha(150),
|
textStyle: TextStyle(
|
||||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
color: textColor,
|
||||||
decoration: TextDecoration.underline,
|
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||||
),
|
decoration:
|
||||||
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
event.redacted ? TextDecoration.lineThrough : null,
|
||||||
);
|
),
|
||||||
|
linkStyle: TextStyle(
|
||||||
|
color: textColor.withAlpha(150),
|
||||||
|
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
case EventTypes.CallInvite:
|
case EventTypes.CallInvite:
|
||||||
return _ButtonContent(
|
return FutureBuilder<User?>(
|
||||||
label: L10n.of(context)!.startedACall(event.sender.calcDisplayname()),
|
future: event.fetchSenderUser(),
|
||||||
icon: const Icon(Icons.phone_outlined),
|
builder: (context, snapshot) {
|
||||||
textColor: buttonTextColor,
|
return _ButtonContent(
|
||||||
onPressed: () => onInfoTab!(event),
|
label: L10n.of(context)!.startedACall(
|
||||||
);
|
snapshot.data?.calcDisplayname() ??
|
||||||
|
event.senderFromMemoryOrFallback.calcDisplayname()),
|
||||||
|
icon: const Icon(Icons.phone_outlined),
|
||||||
|
textColor: buttonTextColor,
|
||||||
|
onPressed: () => onInfoTab!(event),
|
||||||
|
);
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return _ButtonContent(
|
return FutureBuilder<User?>(
|
||||||
label: L10n.of(context)!
|
future: event.fetchSenderUser(),
|
||||||
.userSentUnknownEvent(event.sender.calcDisplayname(), event.type),
|
builder: (context, snapshot) {
|
||||||
icon: const Icon(Icons.info_outlined),
|
return _ButtonContent(
|
||||||
textColor: buttonTextColor,
|
label: L10n.of(context)!.userSentUnknownEvent(
|
||||||
onPressed: () => onInfoTab!(event),
|
snapshot.data?.calcDisplayname() ??
|
||||||
);
|
event.senderFromMemoryOrFallback.calcDisplayname(),
|
||||||
|
event.type),
|
||||||
|
icon: const Icon(Icons.info_outlined),
|
||||||
|
textColor: buttonTextColor,
|
||||||
|
onPressed: () => onInfoTab!(event),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class MessageReactions extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
reactionMap[key]!.count++;
|
reactionMap[key]!.count++;
|
||||||
reactionMap[key]!.reactors!.add(e.sender);
|
reactionMap[key]!.reactors!.add(e.senderFromMemoryOrFallback);
|
||||||
reactionMap[key]!.reacted |= e.senderId == e.room.client.userID;
|
reactionMap[key]!.reacted |= e.senderId == e.room.client.userID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class ReplyContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replyBody = Text(
|
replyBody = Text(
|
||||||
displayEvent.getLocalizedBody(
|
displayEvent.calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
@ -83,18 +83,25 @@ class ReplyContent extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
FutureBuilder<User?>(
|
||||||
displayEvent.sender.calcDisplayname() + ':',
|
future: displayEvent.fetchSenderUser(),
|
||||||
maxLines: 1,
|
builder: (context, snapshot) {
|
||||||
overflow: TextOverflow.ellipsis,
|
return Text(
|
||||||
style: TextStyle(
|
(snapshot.data?.calcDisplayname() ??
|
||||||
fontWeight: FontWeight.bold,
|
displayEvent.senderFromMemoryOrFallback
|
||||||
color: ownMessage
|
.calcDisplayname()) +
|
||||||
? Theme.of(context).colorScheme.onPrimary
|
':',
|
||||||
: Theme.of(context).colorScheme.onBackground,
|
maxLines: 1,
|
||||||
fontSize: fontSize,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
style: TextStyle(
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ownMessage
|
||||||
|
? Theme.of(context).colorScheme.onPrimary
|
||||||
|
: Theme.of(context).colorScheme.onBackground,
|
||||||
|
fontSize: fontSize,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
replyBody,
|
replyBody,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -39,16 +39,24 @@ class StateMessage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
FutureBuilder<String>(
|
||||||
event.getLocalizedBody(MatrixLocals(L10n.of(context)!)),
|
future: event
|
||||||
textAlign: TextAlign.center,
|
.calcLocalizedBody(MatrixLocals(L10n.of(context)!)),
|
||||||
style: TextStyle(
|
builder: (context, snapshot) {
|
||||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
return Text(
|
||||||
color: Theme.of(context).textTheme.bodyText2!.color,
|
snapshot.data ??
|
||||||
decoration:
|
event.calcLocalizedBodyFallback(
|
||||||
event.redacted ? TextDecoration.lineThrough : null,
|
MatrixLocals(L10n.of(context)!)),
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
),
|
style: TextStyle(
|
||||||
|
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||||
|
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
|
decoration: event.redacted
|
||||||
|
? TextDecoration.lineThrough
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
if (counter != 0)
|
if (counter != 0)
|
||||||
Text(
|
Text(
|
||||||
L10n.of(context)!.moreEvents(counter),
|
L10n.of(context)!.moreEvents(counter),
|
||||||
|
@ -26,7 +26,7 @@ class PinnedEvents extends StatelessWidget {
|
|||||||
actions: events
|
actions: events
|
||||||
.map((event) => SheetAction(
|
.map((event) => SheetAction(
|
||||||
key: event?.eventId ?? '',
|
key: event?.eventId ?? '',
|
||||||
label: event?.getLocalizedBody(
|
label: event?.calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: true,
|
withSenderNamePrefix: true,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
@ -90,32 +90,41 @@ class PinnedEvents extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
child: LinkText(
|
child: FutureBuilder<String>(
|
||||||
text: event.getLocalizedBody(
|
future: event.calcLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: true,
|
withSenderNamePrefix: true,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
builder: (context, snapshot) {
|
||||||
textStyle: TextStyle(
|
return LinkText(
|
||||||
overflow: TextOverflow.ellipsis,
|
text: snapshot.data ??
|
||||||
fontSize: fontSize,
|
event.calcLocalizedBodyFallback(
|
||||||
decoration: event.redacted
|
MatrixLocals(L10n.of(context)!),
|
||||||
? TextDecoration.lineThrough
|
withSenderNamePrefix: true,
|
||||||
: null,
|
hideReply: true,
|
||||||
),
|
),
|
||||||
linkStyle: TextStyle(
|
maxLines: 2,
|
||||||
color: Theme.of(context)
|
textStyle: TextStyle(
|
||||||
.textTheme
|
overflow: TextOverflow.ellipsis,
|
||||||
.bodyText1
|
fontSize: fontSize,
|
||||||
?.color
|
decoration: event.redacted
|
||||||
?.withAlpha(150),
|
? TextDecoration.lineThrough
|
||||||
fontSize: fontSize,
|
: null,
|
||||||
decoration: TextDecoration.underline,
|
),
|
||||||
),
|
linkStyle: TextStyle(
|
||||||
onLinkTap: (url) =>
|
color: Theme.of(context)
|
||||||
UrlLauncher(context, url).launchUrl(),
|
.textTheme
|
||||||
),
|
.bodyText1
|
||||||
|
?.color
|
||||||
|
?.withAlpha(150),
|
||||||
|
fontSize: fontSize,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
onLinkTap: (url) =>
|
||||||
|
UrlLauncher(context, url).launchUrl(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -50,6 +50,7 @@ class _EditContent extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final event = this.event;
|
||||||
if (event == null) {
|
if (event == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
@ -60,19 +61,27 @@ class _EditContent extends StatelessWidget {
|
|||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
Container(width: 15.0),
|
Container(width: 15.0),
|
||||||
Text(
|
FutureBuilder<String>(
|
||||||
event?.getLocalizedBody(
|
future: event.calcLocalizedBody(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
hideReply: true,
|
hideReply: true,
|
||||||
) ??
|
),
|
||||||
'',
|
builder: (context, snapshot) {
|
||||||
overflow: TextOverflow.ellipsis,
|
return Text(
|
||||||
maxLines: 1,
|
snapshot.data ??
|
||||||
style: TextStyle(
|
event.calcLocalizedBodyFallback(
|
||||||
color: Theme.of(context).textTheme.bodyText2!.color,
|
MatrixLocals(L10n.of(context)!),
|
||||||
),
|
withSenderNamePrefix: false,
|
||||||
),
|
hideReply: true,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -94,15 +94,18 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Avatar(
|
leading: Avatar(
|
||||||
mxContent: room
|
mxContent: room
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
.unsafeGetUserFromMemoryOrFallback(
|
||||||
|
deviceKeys[i].userId)
|
||||||
.avatarUrl,
|
.avatarUrl,
|
||||||
name: room
|
name: room
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
.unsafeGetUserFromMemoryOrFallback(
|
||||||
|
deviceKeys[i].userId)
|
||||||
.calcDisplayname(),
|
.calcDisplayname(),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
room
|
room
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
.unsafeGetUserFromMemoryOrFallback(
|
||||||
|
deviceKeys[i].userId)
|
||||||
.calcDisplayname(),
|
.calcDisplayname(),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
|
@ -262,32 +262,47 @@ class ChatListItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
)
|
)
|
||||||
: Text(
|
: FutureBuilder<String>(
|
||||||
room.membership == Membership.invite
|
future: room.lastEvent?.calcLocalizedBody(
|
||||||
? L10n.of(context)!.youAreInvitedToThisChat
|
MatrixLocals(L10n.of(context)!),
|
||||||
: room.lastEvent?.getLocalizedBody(
|
hideReply: true,
|
||||||
MatrixLocals(L10n.of(context)!),
|
hideEdit: true,
|
||||||
hideReply: true,
|
plaintextBody: true,
|
||||||
hideEdit: true,
|
removeMarkdown: true,
|
||||||
plaintextBody: true,
|
withSenderNamePrefix: !room.isDirectChat ||
|
||||||
removeMarkdown: true,
|
room.directChatMatrixID !=
|
||||||
withSenderNamePrefix: !room.isDirectChat ||
|
room.lastEvent?.senderId,
|
||||||
room.directChatMatrixID !=
|
) ??
|
||||||
room.lastEvent?.senderId,
|
Future.value(L10n.of(context)!.emptyChat),
|
||||||
) ??
|
builder: (context, snapshot) {
|
||||||
L10n.of(context)!.emptyChat,
|
return Text(
|
||||||
softWrap: false,
|
room.membership == Membership.invite
|
||||||
maxLines: 1,
|
? L10n.of(context)!.youAreInvitedToThisChat
|
||||||
overflow: TextOverflow.ellipsis,
|
: snapshot.data ??
|
||||||
style: TextStyle(
|
room.lastEvent?.calcLocalizedBodyFallback(
|
||||||
color: unread
|
MatrixLocals(L10n.of(context)!),
|
||||||
? Theme.of(context).colorScheme.secondary
|
hideReply: true,
|
||||||
: Theme.of(context).textTheme.bodyText2!.color,
|
hideEdit: true,
|
||||||
decoration: room.lastEvent?.redacted == true
|
plaintextBody: true,
|
||||||
? TextDecoration.lineThrough
|
removeMarkdown: true,
|
||||||
: null,
|
withSenderNamePrefix: !room.isDirectChat ||
|
||||||
),
|
room.directChatMatrixID !=
|
||||||
),
|
room.lastEvent?.senderId,
|
||||||
|
) ??
|
||||||
|
L10n.of(context)!.emptyChat,
|
||||||
|
softWrap: false,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: unread
|
||||||
|
? Theme.of(context).colorScheme.secondary
|
||||||
|
: Theme.of(context).textTheme.bodyText2!.color,
|
||||||
|
decoration: room.lastEvent?.redacted == true
|
||||||
|
? TextDecoration.lineThrough
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
|
@ -48,7 +48,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
(client) => PopupMenuItem(
|
(client) => PopupMenuItem(
|
||||||
value: client,
|
value: client,
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: client!.ownProfile,
|
future: client!.fetchOwnProfile(),
|
||||||
builder: (context, snapshot) => Row(
|
builder: (context, snapshot) => Row(
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
@ -90,7 +90,7 @@ class ClientChooserButton extends StatelessWidget {
|
|||||||
matrix.accountBundles.forEach((key, value) => clientCount += value.length);
|
matrix.accountBundles.forEach((key, value) => clientCount += value.length);
|
||||||
return Center(
|
return Center(
|
||||||
child: FutureBuilder<Profile>(
|
child: FutureBuilder<Profile>(
|
||||||
future: matrix.client.ownProfile,
|
future: matrix.client.fetchOwnProfile(),
|
||||||
builder: (context, snapshot) => Stack(
|
builder: (context, snapshot) => Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
@ -38,7 +38,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
|
|||||||
final participantsIds = participants.map((p) => p.stateKey).toList();
|
final participantsIds = participants.map((p) => p.stateKey).toList();
|
||||||
final contacts = client.rooms
|
final contacts = client.rooms
|
||||||
.where((r) => r.isDirectChat)
|
.where((r) => r.isDirectChat)
|
||||||
.map((r) => r.getUserByMXIDSync(r.directChatMatrixID!))
|
.map((r) => r.unsafeGetUserFromMemoryOrFallback(r.directChatMatrixID!))
|
||||||
.toList()
|
.toList()
|
||||||
..removeWhere((u) => participantsIds.contains(u.stateKey));
|
..removeWhere((u) => participantsIds.contains(u.stateKey));
|
||||||
contacts.sort(
|
contacts.sort(
|
||||||
|
@ -111,7 +111,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
|
|||||||
if (directChatId != null) {
|
if (directChatId != null) {
|
||||||
user = widget.request.client
|
user = widget.request.client
|
||||||
.getRoomById(directChatId)!
|
.getRoomById(directChatId)!
|
||||||
.getUserByMXIDSync(widget.request.userId);
|
.unsafeGetUserFromMemoryOrFallback(widget.request.userId);
|
||||||
}
|
}
|
||||||
final displayName =
|
final displayName =
|
||||||
user?.calcDisplayname() ?? widget.request.userId.localpart!;
|
user?.calcDisplayname() ?? widget.request.userId.localpart!;
|
||||||
|
@ -46,13 +46,11 @@ class SearchView extends StatelessWidget {
|
|||||||
}).then((QueryPublicRoomsResponse res) {
|
}).then((QueryPublicRoomsResponse res) {
|
||||||
final genericSearchTerm = controller.genericSearchTerm;
|
final genericSearchTerm = controller.genericSearchTerm;
|
||||||
if (genericSearchTerm != null &&
|
if (genericSearchTerm != null &&
|
||||||
!res.chunk.any((room) =>
|
!res.chunk.any(
|
||||||
(room.aliases?.contains(controller.genericSearchTerm) ?? false) ||
|
(room) => room.canonicalAlias == controller.genericSearchTerm)) {
|
||||||
room.canonicalAlias == controller.genericSearchTerm)) {
|
|
||||||
// we have to tack on the original alias
|
// we have to tack on the original alias
|
||||||
res.chunk.add(
|
res.chunk.add(
|
||||||
PublicRoomsChunk(
|
PublicRoomsChunk(
|
||||||
aliases: [genericSearchTerm],
|
|
||||||
name: genericSearchTerm,
|
name: genericSearchTerm,
|
||||||
numJoinedMembers: 0,
|
numJoinedMembers: 0,
|
||||||
roomId: '!unknown',
|
roomId: '!unknown',
|
||||||
|
@ -370,7 +370,7 @@ class StoryPageController extends State<StoryPage> {
|
|||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId)
|
||||||
?.getState(EventTypes.RoomCreate)
|
?.getState(EventTypes.RoomCreate)
|
||||||
?.sender
|
?.senderFromMemoryOrFallback
|
||||||
.avatarUrl;
|
.avatarUrl;
|
||||||
|
|
||||||
String get title =>
|
String get title =>
|
||||||
@ -378,7 +378,7 @@ class StoryPageController extends State<StoryPage> {
|
|||||||
.client
|
.client
|
||||||
.getRoomById(roomId)
|
.getRoomById(roomId)
|
||||||
?.getState(EventTypes.RoomCreate)
|
?.getState(EventTypes.RoomCreate)
|
||||||
?.sender
|
?.senderFromMemoryOrFallback
|
||||||
.calcDisplayname() ??
|
.calcDisplayname() ??
|
||||||
'Story not found';
|
'Story not found';
|
||||||
|
|
||||||
@ -485,7 +485,8 @@ class StoryPageController extends State<StoryPage> {
|
|||||||
case PopupStoryAction.message:
|
case PopupStoryAction.message:
|
||||||
final roomIdResult = await showFutureLoadingDialog(
|
final roomIdResult = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => currentEvent!.sender.startDirectChat(),
|
future: () =>
|
||||||
|
currentEvent!.senderFromMemoryOrFallback.startDirectChat(),
|
||||||
);
|
);
|
||||||
if (roomIdResult.error != null) return;
|
if (roomIdResult.error != null) return;
|
||||||
VRouter.of(context).toSegments(['rooms', roomIdResult.result!]);
|
VRouter.of(context).toSegments(['rooms', roomIdResult.result!]);
|
||||||
|
@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/utils/custom_image_resizer.dart';
|
import 'package:fluffychat/utils/custom_image_resizer.dart';
|
||||||
|
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
import 'famedlysdk_store.dart';
|
import 'famedlysdk_store.dart';
|
||||||
import 'matrix_sdk_extensions.dart/fluffybox_database.dart';
|
import 'matrix_sdk_extensions.dart/fluffybox_database.dart';
|
||||||
import 'matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart';
|
|
||||||
|
|
||||||
abstract class ClientManager {
|
abstract class ClientManager {
|
||||||
static const String clientNamespace = 'im.fluffychat.store.clients';
|
static const String clientNamespace = 'im.fluffychat.store.clients';
|
||||||
@ -95,8 +95,8 @@ abstract class ClientManager {
|
|||||||
// To check which story room we can post in
|
// To check which story room we can post in
|
||||||
EventTypes.RoomPowerLevels,
|
EventTypes.RoomPowerLevels,
|
||||||
},
|
},
|
||||||
databaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder,
|
databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
|
||||||
legacyDatabaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
|
legacyDatabaseBuilder: FlutterFluffyBoxDatabase.databaseBuilder,
|
||||||
supportedLoginTypes: {
|
supportedLoginTypes: {
|
||||||
AuthenticationTypes.password,
|
AuthenticationTypes.password,
|
||||||
if (PlatformInfos.isMobile ||
|
if (PlatformInfos.isMobile ||
|
||||||
|
@ -12,7 +12,8 @@ extension ClientStoriesExtension on Client {
|
|||||||
|
|
||||||
List<User> get contacts => rooms
|
List<User> get contacts => rooms
|
||||||
.where((room) => room.isDirectChat)
|
.where((room) => room.isDirectChat)
|
||||||
.map((room) => room.getUserByMXIDSync(room.directChatMatrixID!))
|
.map((room) =>
|
||||||
|
room.unsafeGetUserFromMemoryOrFallback(room.directChatMatrixID!))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
List<Room> get storiesRooms => rooms
|
List<Room> get storiesRooms => rooms
|
||||||
|
@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
import '../client_manager.dart';
|
import '../client_manager.dart';
|
||||||
import '../famedlysdk_store.dart';
|
import '../famedlysdk_store.dart';
|
||||||
|
|
||||||
|
// ignore: deprecated_member_use
|
||||||
class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
|
class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
|
||||||
FlutterFluffyBoxDatabase(
|
FlutterFluffyBoxDatabase(
|
||||||
String name,
|
String name,
|
||||||
@ -27,6 +28,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
|
|||||||
|
|
||||||
static const String _cipherStorageKey = 'database_encryption_key';
|
static const String _cipherStorageKey = 'database_encryption_key';
|
||||||
|
|
||||||
|
// ignore: deprecated_member_use
|
||||||
static Future<FluffyBoxDatabase> databaseBuilder(Client client) async {
|
static Future<FluffyBoxDatabase> databaseBuilder(Client client) async {
|
||||||
Logs().d('Open FluffyBox...');
|
Logs().d('Open FluffyBox...');
|
||||||
fluffybox.HiveAesCipher? hiverCipher;
|
fluffybox.HiveAesCipher? hiverCipher;
|
||||||
@ -59,6 +61,7 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore: deprecated_member_use
|
||||||
final db = FluffyBoxDatabase(
|
final db = FluffyBoxDatabase(
|
||||||
'fluffybox_${client.clientName.replaceAll(' ', '_').toLowerCase()}',
|
'fluffybox_${client.clientName.replaceAll(' ', '_').toLowerCase()}',
|
||||||
await _findDatabasePath(client),
|
await _findDatabasePath(client),
|
||||||
|
@ -2,78 +2,108 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart' hide Key;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
import '../platform_infos.dart';
|
class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase {
|
||||||
|
FlutterHiveCollectionsDatabase(
|
||||||
class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
|
String name,
|
||||||
FlutterMatrixHiveStore(String name, {HiveCipher? encryptionCipher})
|
String path, {
|
||||||
: super(
|
HiveCipher? key,
|
||||||
|
}) : super(
|
||||||
name,
|
name,
|
||||||
encryptionCipher: encryptionCipher,
|
path,
|
||||||
|
key: key,
|
||||||
);
|
);
|
||||||
|
|
||||||
static bool _hiveInitialized = false;
|
static const String _cipherStorageKey = 'database_encryption_key';
|
||||||
static const String _hiveCipherStorageKey = 'hive_encryption_key';
|
|
||||||
|
|
||||||
static Future<FamedlySdkHiveDatabase> hiveDatabaseBuilder(
|
static Future<FlutterHiveCollectionsDatabase> databaseBuilder(
|
||||||
Client client) async {
|
Client client) async {
|
||||||
if (!kIsWeb && !_hiveInitialized) {
|
Logs().d('Open Hive...');
|
||||||
_hiveInitialized = true;
|
HiveAesCipher? hiverCipher;
|
||||||
}
|
|
||||||
HiveCipher? hiverCipher;
|
|
||||||
try {
|
try {
|
||||||
// Workaround for secure storage is calling Platform.operatingSystem on web
|
// Workaround for secure storage is calling Platform.operatingSystem on web
|
||||||
if (kIsWeb || Platform.isLinux) throw MissingPluginException();
|
if (kIsWeb) throw MissingPluginException();
|
||||||
|
|
||||||
const secureStorage = FlutterSecureStorage();
|
const secureStorage = FlutterSecureStorage();
|
||||||
final containsEncryptionKey =
|
final containsEncryptionKey =
|
||||||
await secureStorage.containsKey(key: _hiveCipherStorageKey);
|
await secureStorage.containsKey(key: _cipherStorageKey);
|
||||||
if (!containsEncryptionKey) {
|
if (!containsEncryptionKey) {
|
||||||
|
// do not try to create a buggy secure storage for new Linux users
|
||||||
|
if (Platform.isLinux) throw MissingPluginException();
|
||||||
final key = Hive.generateSecureKey();
|
final key = Hive.generateSecureKey();
|
||||||
await secureStorage.write(
|
await secureStorage.write(
|
||||||
key: _hiveCipherStorageKey,
|
key: _cipherStorageKey,
|
||||||
value: base64UrlEncode(key),
|
value: base64UrlEncode(key),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround for if we just wrote to the key and it still doesn't exist
|
// workaround for if we just wrote to the key and it still doesn't exist
|
||||||
final rawEncryptionKey =
|
final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey);
|
||||||
await secureStorage.read(key: _hiveCipherStorageKey);
|
|
||||||
if (rawEncryptionKey == null) throw MissingPluginException();
|
if (rawEncryptionKey == null) throw MissingPluginException();
|
||||||
|
|
||||||
final encryptionKey = base64Url.decode(rawEncryptionKey);
|
hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey));
|
||||||
hiverCipher = HiveAesCipher(encryptionKey);
|
|
||||||
} on MissingPluginException catch (_) {
|
} on MissingPluginException catch (_) {
|
||||||
Logs().i('Hive encryption is not supported on this platform');
|
Logs().i('Hive encryption is not supported on this platform');
|
||||||
|
} catch (_) {
|
||||||
|
const FlutterSecureStorage().delete(key: _cipherStorageKey);
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
final db = FlutterMatrixHiveStore(
|
|
||||||
client.clientName,
|
final db = FlutterHiveCollectionsDatabase(
|
||||||
encryptionCipher: hiverCipher,
|
'hive_collections_${client.clientName.replaceAll(' ', '_').toLowerCase()}',
|
||||||
|
await _findDatabasePath(client),
|
||||||
|
key: hiverCipher,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await db.open();
|
await db.open();
|
||||||
} catch (e, s) {
|
} catch (_) {
|
||||||
Logs().e('Unable to open Hive. Delete and try again...', e, s);
|
Logs().w('Unable to open Hive. Delete database and storage key...');
|
||||||
|
const FlutterSecureStorage().delete(key: _cipherStorageKey);
|
||||||
await db.clear();
|
await db.clear();
|
||||||
await db.open();
|
rethrow;
|
||||||
}
|
}
|
||||||
|
Logs().d('Hive is ready');
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<String> _findDatabasePath(Client client) async {
|
||||||
|
String path = client.clientName;
|
||||||
|
if (!kIsWeb) {
|
||||||
|
Directory directory;
|
||||||
|
try {
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
directory = await getApplicationSupportDirectory();
|
||||||
|
} else {
|
||||||
|
directory = await getApplicationDocumentsDirectory();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
directory = await getLibraryDirectory();
|
||||||
|
} catch (_) {
|
||||||
|
directory = Directory.current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do not destroy your stable FluffyChat in debug mode
|
||||||
|
if (kDebugMode) {
|
||||||
|
directory = Directory(directory.uri.resolve('debug').toFilePath());
|
||||||
|
directory.create(recursive: true);
|
||||||
|
}
|
||||||
|
path = directory.path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0;
|
int get maxFileSize => supportsFileStoring ? 100 * 1024 * 1024 : 0;
|
||||||
@override
|
@override
|
||||||
bool get supportsFileStoring => (PlatformInfos.isIOS ||
|
bool get supportsFileStoring => !kIsWeb;
|
||||||
PlatformInfos.isAndroid ||
|
|
||||||
PlatformInfos.isDesktop);
|
|
||||||
|
|
||||||
Future<String> _getFileStoreDirectory() async {
|
Future<String> _getFileStoreDirectory() async {
|
||||||
try {
|
try {
|
@ -62,7 +62,7 @@ Future<void> pushHelper(
|
|||||||
final matrixLocals = MatrixLocals(l10n);
|
final matrixLocals = MatrixLocals(l10n);
|
||||||
|
|
||||||
// Calculate the body
|
// Calculate the body
|
||||||
final body = event.getLocalizedBody(
|
final body = await event.calcLocalizedBody(
|
||||||
matrixLocals,
|
matrixLocals,
|
||||||
plaintextBody: true,
|
plaintextBody: true,
|
||||||
withSenderNamePrefix: !event.room.isDirectChat,
|
withSenderNamePrefix: !event.room.isDirectChat,
|
||||||
|
@ -31,7 +31,7 @@ extension LocalNotificationsExtension on MatrixState {
|
|||||||
final event = Event.fromJson(eventUpdate.content, room);
|
final event = Event.fromJson(eventUpdate.content, room);
|
||||||
final title =
|
final title =
|
||||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!));
|
room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!));
|
||||||
final body = event.getLocalizedBody(
|
final body = await event.calcLocalizedBody(
|
||||||
MatrixLocals(L10n.of(widget.context)!),
|
MatrixLocals(L10n.of(widget.context)!),
|
||||||
withSenderNamePrefix:
|
withSenderNamePrefix:
|
||||||
!room.isDirectChat || room.lastEvent?.senderId == client.userID,
|
!room.isDirectChat || room.lastEvent?.senderId == client.userID,
|
||||||
@ -40,8 +40,11 @@ extension LocalNotificationsExtension on MatrixState {
|
|||||||
hideEdit: true,
|
hideEdit: true,
|
||||||
removeMarkdown: true,
|
removeMarkdown: true,
|
||||||
);
|
);
|
||||||
final icon = event.sender.avatarUrl?.getThumbnail(client,
|
final icon = event.senderFromMemoryOrFallback.avatarUrl?.getThumbnail(
|
||||||
width: 64, height: 64, method: ThumbnailMethod.crop) ??
|
client,
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
method: ThumbnailMethod.crop) ??
|
||||||
room.avatar?.getThumbnail(client,
|
room.avatar?.getThumbnail(client,
|
||||||
width: 64, height: 64, method: ThumbnailMethod.crop);
|
width: 64, height: 64, method: ThumbnailMethod.crop);
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
|
@ -41,9 +41,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _testRoom(PublicRoomsChunk r) =>
|
bool _testRoom(PublicRoomsChunk r) => r.canonicalAlias == roomAlias;
|
||||||
r.canonicalAlias == roomAlias ||
|
|
||||||
(r.aliases?.contains(roomAlias) ?? false);
|
|
||||||
|
|
||||||
Future<PublicRoomsChunk> _search(BuildContext context) async {
|
Future<PublicRoomsChunk> _search(BuildContext context) async {
|
||||||
final chunk = this.chunk;
|
final chunk = this.chunk;
|
||||||
|
15
pubspec.lock
15
pubspec.lock
@ -395,6 +395,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.1"
|
||||||
|
enhanced_enum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: enhanced_enum
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -788,7 +795,7 @@ packages:
|
|||||||
name: hive
|
name: hive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.2.1"
|
||||||
hive_flutter:
|
hive_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1012,21 +1019,21 @@ packages:
|
|||||||
name: matrix
|
name: matrix
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.6"
|
version: "0.9.12"
|
||||||
matrix_api_lite:
|
matrix_api_lite:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matrix_api_lite
|
name: matrix_api_lite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.3"
|
version: "1.1.1"
|
||||||
matrix_homeserver_recommendations:
|
matrix_homeserver_recommendations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: matrix_homeserver_recommendations
|
name: matrix_homeserver_recommendations
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
matrix_link_text:
|
matrix_link_text:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -57,7 +57,7 @@ dependencies:
|
|||||||
keyboard_shortcuts: ^0.1.4
|
keyboard_shortcuts: ^0.1.4
|
||||||
localstorage: ^4.0.0+1
|
localstorage: ^4.0.0+1
|
||||||
lottie: ^1.2.2
|
lottie: ^1.2.2
|
||||||
matrix: ^0.9.4
|
matrix: ^0.9.12
|
||||||
matrix_homeserver_recommendations: ^0.2.0
|
matrix_homeserver_recommendations: ^0.2.0
|
||||||
matrix_link_text: ^1.0.2
|
matrix_link_text: ^1.0.2
|
||||||
native_imaging:
|
native_imaging:
|
||||||
|
@ -2,7 +2,7 @@ import 'package:matrix/encryption/utils/key_verification.dart';
|
|||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:matrix_api_lite/fake_matrix_api.dart';
|
import 'package:matrix_api_lite/fake_matrix_api.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_matrix_hive_database.dart';
|
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/flutter_hive_collections_database.dart';
|
||||||
|
|
||||||
Future<Client> prepareTestClient({
|
Future<Client> prepareTestClient({
|
||||||
bool loggedIn = false,
|
bool loggedIn = false,
|
||||||
@ -20,7 +20,7 @@ Future<Client> prepareTestClient({
|
|||||||
importantStateEvents: <String>{
|
importantStateEvents: <String>{
|
||||||
'im.ponies.room_emotes', // we want emotes to work properly
|
'im.ponies.room_emotes', // we want emotes to work properly
|
||||||
},
|
},
|
||||||
databaseBuilder: FlutterMatrixHiveStore.hiveDatabaseBuilder,
|
databaseBuilder: FlutterHiveCollectionsDatabase.databaseBuilder,
|
||||||
supportedLoginTypes: {
|
supportedLoginTypes: {
|
||||||
AuthenticationTypes.password,
|
AuthenticationTypes.password,
|
||||||
AuthenticationTypes.sso
|
AuthenticationTypes.sso
|
||||||
|
Loading…
Reference in New Issue
Block a user