mirror of
https://github.com/krille-chan/fluffychat
synced 2024-10-05 14:32:43 +00:00
Merge branch 'main' into flutter-3.22
This commit is contained in:
commit
d9edf9f05b
17 changed files with 411 additions and 344 deletions
|
@ -156,7 +156,8 @@ GEM
|
|||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rexml (3.2.8)
|
||||
strscan (>= 3.0.9)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.4)
|
||||
rubyzip (2.3.0)
|
||||
|
@ -169,6 +170,7 @@ GEM
|
|||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
strscan (3.1.0)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
|
|
|
@ -610,7 +610,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
}
|
||||
}
|
||||
|
||||
if (await Record().hasPermission() == false) return;
|
||||
if (await AudioRecorder().hasPermission() == false) return;
|
||||
final result = await showDialog<RecordingResult>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
|
|
|
@ -150,7 +150,8 @@ class ChatView extends StatelessWidget {
|
|||
}
|
||||
},
|
||||
child: StreamBuilder(
|
||||
stream: controller.room.onUpdate.stream
|
||||
stream: controller.room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == controller.room.id)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, snapshot) => FutureBuilder(
|
||||
future: controller.loadTimelineFuture,
|
||||
|
|
|
@ -144,255 +144,276 @@ class Message extends StatelessWidget {
|
|||
setState(resetAnimateIn);
|
||||
});
|
||||
}
|
||||
return AnimatedSlide(
|
||||
offset: Offset(0, animateIn ? 1 : 0),
|
||||
return AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: InkWell(
|
||||
onTap: () => onSelect(event),
|
||||
onLongPress: () => onSelect(event),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: selected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
.withAlpha(100)
|
||||
: highlightMarker
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiaryContainer
|
||||
.withAlpha(100)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: rowMainAxisAlignment,
|
||||
children: [
|
||||
if (longPressSelect)
|
||||
SizedBox(
|
||||
height: 32,
|
||||
width: Avatar.defaultSize,
|
||||
child: Checkbox.adaptive(
|
||||
value: selected,
|
||||
shape: const CircleBorder(),
|
||||
onChanged: (_) => onSelect(event),
|
||||
),
|
||||
)
|
||||
else if (nextEventSameSender || ownMessage)
|
||||
SizedBox(
|
||||
width: Avatar.defaultSize,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: event.status == EventStatus.error
|
||||
? const Icon(Icons.error, color: Colors.red)
|
||||
: event.fileSendingStatus != null
|
||||
? const CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 1,
|
||||
)
|
||||
: null,
|
||||
clipBehavior: Clip.none,
|
||||
alignment: ownMessage ? Alignment.bottomRight : Alignment.bottomLeft,
|
||||
child: animateIn
|
||||
? const SizedBox(height: 0, width: double.infinity)
|
||||
: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: InkWell(
|
||||
onTap: () => onSelect(event),
|
||||
onLongPress: () => onSelect(event),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: selected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
.withAlpha(100)
|
||||
: highlightMarker
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.tertiaryContainer
|
||||
.withAlpha(100)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final user =
|
||||
snapshot.data ?? event.senderFromMemoryOrFallback;
|
||||
return Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
presenceUserId: user.stateKey,
|
||||
presenceBackgroundColor:
|
||||
avatarPresenceBackgroundColor,
|
||||
onTap: () => onAvatarTab(event),
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: rowMainAxisAlignment,
|
||||
children: [
|
||||
if (!nextEventSameSender)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 8.0, bottom: 4),
|
||||
child: ownMessage || event.room.isDirectChat
|
||||
? const SizedBox(height: 12)
|
||||
: FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data?.calcDisplayname() ??
|
||||
event.senderFromMemoryOrFallback
|
||||
.calcDisplayname();
|
||||
return Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color:
|
||||
(Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? displayname.color
|
||||
: displayname.lightColorText),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (longPressSelect)
|
||||
SizedBox(
|
||||
height: 32,
|
||||
width: Avatar.defaultSize,
|
||||
child: Checkbox.adaptive(
|
||||
value: selected,
|
||||
shape: const CircleBorder(),
|
||||
onChanged: (_) => onSelect(event),
|
||||
),
|
||||
)
|
||||
else if (nextEventSameSender || ownMessage)
|
||||
SizedBox(
|
||||
width: Avatar.defaultSize,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: event.status == EventStatus.error
|
||||
? const Icon(Icons.error, color: Colors.red)
|
||||
: event.fileSendingStatus != null
|
||||
? const CircularProgressIndicator
|
||||
.adaptive(
|
||||
strokeWidth: 1,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final user = snapshot.data ??
|
||||
event.senderFromMemoryOrFallback;
|
||||
return Avatar(
|
||||
mxContent: user.avatarUrl,
|
||||
name: user.calcDisplayname(),
|
||||
presenceUserId: user.stateKey,
|
||||
presenceBackgroundColor:
|
||||
avatarPresenceBackgroundColor,
|
||||
onTap: () => onAvatarTab(event),
|
||||
);
|
||||
},
|
||||
),
|
||||
Container(
|
||||
alignment: alignment,
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: GestureDetector(
|
||||
onLongPress: longPressSelect
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
onSelect(event);
|
||||
},
|
||||
child: AnimatedOpacity(
|
||||
opacity: animateIn
|
||||
? 0
|
||||
: event.redacted ||
|
||||
event.messageType ==
|
||||
MessageTypes.BadEncrypted ||
|
||||
event.status.isSending
|
||||
? 0.5
|
||||
: 1,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
color: noBubble ? Colors.transparent : color,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!nextEventSameSender)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
bottom: 4,
|
||||
),
|
||||
padding: noBubble || noPadding
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (event.relationshipType ==
|
||||
RelationshipTypes.reply)
|
||||
FutureBuilder<Event?>(
|
||||
future: event.getReplyEvent(timeline),
|
||||
builder:
|
||||
(BuildContext context, snapshot) {
|
||||
final replyEvent = snapshot.hasData
|
||||
? snapshot.data!
|
||||
: Event(
|
||||
eventId: event
|
||||
.relationshipEventId!,
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId: event.senderId,
|
||||
type: 'm.room.message',
|
||||
room: event.room,
|
||||
status: EventStatus.sent,
|
||||
originServerTs:
|
||||
DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 4.0,
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius:
|
||||
ReplyContent.borderRadius,
|
||||
onTap: () => scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
),
|
||||
child: ownMessage || event.room.isDirectChat
|
||||
? const SizedBox(height: 12)
|
||||
: FutureBuilder<User?>(
|
||||
future: event.fetchSenderUser(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname = snapshot.data
|
||||
?.calcDisplayname() ??
|
||||
event.senderFromMemoryOrFallback
|
||||
.calcDisplayname();
|
||||
return Text(
|
||||
displayname,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: (Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? displayname.color
|
||||
: displayname
|
||||
.lightColorText),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
MessageContent(
|
||||
displayEvent,
|
||||
textColor: textColor,
|
||||
onInfoTab: onInfoTab,
|
||||
),
|
||||
Container(
|
||||
alignment: alignment,
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: GestureDetector(
|
||||
onLongPress: longPressSelect
|
||||
? null
|
||||
: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
onSelect(event);
|
||||
},
|
||||
child: AnimatedOpacity(
|
||||
opacity: animateIn
|
||||
? 0
|
||||
: event.redacted ||
|
||||
event.messageType ==
|
||||
MessageTypes.BadEncrypted ||
|
||||
event.status.isSending
|
||||
? 0.5
|
||||
: 1,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Material(
|
||||
color:
|
||||
noBubble ? Colors.transparent : color,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
textColor.withAlpha(164),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
],
|
||||
padding: noBubble || noPadding
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth:
|
||||
FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (event.relationshipType ==
|
||||
RelationshipTypes.reply)
|
||||
FutureBuilder<Event?>(
|
||||
future: event
|
||||
.getReplyEvent(timeline),
|
||||
builder: (
|
||||
BuildContext context,
|
||||
snapshot,
|
||||
) {
|
||||
final replyEvent = snapshot
|
||||
.hasData
|
||||
? snapshot.data!
|
||||
: Event(
|
||||
eventId: event
|
||||
.relationshipEventId!,
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId:
|
||||
event.senderId,
|
||||
type:
|
||||
'm.room.message',
|
||||
room: event.room,
|
||||
status:
|
||||
EventStatus.sent,
|
||||
originServerTs:
|
||||
DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
bottom: 4.0,
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: ReplyContent
|
||||
.borderRadius,
|
||||
onTap: () =>
|
||||
scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage:
|
||||
ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
MessageContent(
|
||||
displayEvent,
|
||||
textColor: textColor,
|
||||
onInfoTab: onInfoTab,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor
|
||||
.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color: textColor
|
||||
.withAlpha(164),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -9,7 +9,6 @@ import 'package:pasteboard/pasteboard.dart';
|
|||
import 'package:slugify/slugify.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
import '../../widgets/avatar.dart';
|
||||
|
@ -465,8 +464,11 @@ class InputBar extends StatelessWidget {
|
|||
mimeType: content.mimeType,
|
||||
bytes: data,
|
||||
name: content.uri.split('/').last,
|
||||
).detectFileType;
|
||||
room.sendFileEvent(file, shrinkImageMaxDimension: 1600);
|
||||
);
|
||||
room.sendFileEvent(
|
||||
file,
|
||||
shrinkImageMaxDimension: 1600,
|
||||
);
|
||||
},
|
||||
),
|
||||
minLines: minLines,
|
||||
|
|
|
@ -28,16 +28,16 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
bool error = false;
|
||||
String? _recordedPath;
|
||||
final _audioRecorder = Record();
|
||||
final _audioRecorder = AudioRecorder();
|
||||
final List<double> amplitudeTimeline = [];
|
||||
|
||||
static const int bitRate = 64000;
|
||||
static const int samplingRate = 22050;
|
||||
static const int samplingRate = 44100;
|
||||
|
||||
Future<void> startRecording() async {
|
||||
try {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
_recordedPath =
|
||||
final path = _recordedPath =
|
||||
'${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
|
||||
final result = await _audioRecorder.hasPermission();
|
||||
|
@ -47,9 +47,15 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
}
|
||||
await WakelockPlus.enable();
|
||||
await _audioRecorder.start(
|
||||
path: _recordedPath,
|
||||
bitRate: bitRate,
|
||||
samplingRate: samplingRate,
|
||||
const RecordConfig(
|
||||
bitRate: bitRate,
|
||||
sampleRate: samplingRate,
|
||||
numChannels: 1,
|
||||
autoGain: true,
|
||||
echoCancel: true,
|
||||
noiseSuppress: true,
|
||||
),
|
||||
path: path,
|
||||
);
|
||||
setState(() => _duration = Duration.zero);
|
||||
_recorderSubscription?.cancel();
|
||||
|
|
|
@ -14,80 +14,90 @@ class TypingIndicators extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final typingUsers = controller.room.typingUsers
|
||||
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
|
||||
const topPadding = 20.0;
|
||||
const bottomPadding = 4.0;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedContainer(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 2.5),
|
||||
height: typingUsers.isEmpty ? 0 : Avatar.defaultSize + bottomPadding,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
bottom: bottomPadding,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Avatar.defaultSize,
|
||||
width: typingUsers.length < 2
|
||||
? Avatar.defaultSize
|
||||
: Avatar.defaultSize + 16,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (typingUsers.isNotEmpty)
|
||||
Avatar(
|
||||
mxContent: typingUsers.first.avatarUrl,
|
||||
name: typingUsers.first.calcDisplayname(),
|
||||
),
|
||||
if (typingUsers.length == 2)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Avatar(
|
||||
mxContent: typingUsers.length == 2
|
||||
? typingUsers.last.avatarUrl
|
||||
: null,
|
||||
name: typingUsers.length == 2
|
||||
? typingUsers.last.calcDisplayname()
|
||||
: '+${typingUsers.length - 1}',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: topPadding),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(2),
|
||||
topRight: Radius.circular(AppConfig.borderRadius),
|
||||
bottomLeft: Radius.circular(AppConfig.borderRadius),
|
||||
bottomRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: typingUsers.isEmpty ? null : const _TypingDots(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
return StreamBuilder<Object>(
|
||||
stream: controller.room.client.onSync.stream.where(
|
||||
(syncUpdate) =>
|
||||
syncUpdate.rooms?.join?[controller.room.id]?.ephemeral
|
||||
?.any((ephemeral) => ephemeral.type == 'm.typing') ??
|
||||
false,
|
||||
),
|
||||
builder: (context, _) {
|
||||
final typingUsers = controller.room.typingUsers
|
||||
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
|
||||
const topPadding = 20.0;
|
||||
const bottomPadding = 4.0;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedContainer(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 2.5),
|
||||
height:
|
||||
typingUsers.isEmpty ? 0 : Avatar.defaultSize + bottomPadding,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
bottom: bottomPadding,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Avatar.defaultSize,
|
||||
width: typingUsers.length < 2
|
||||
? Avatar.defaultSize
|
||||
: Avatar.defaultSize + 16,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (typingUsers.isNotEmpty)
|
||||
Avatar(
|
||||
mxContent: typingUsers.first.avatarUrl,
|
||||
name: typingUsers.first.calcDisplayname(),
|
||||
),
|
||||
if (typingUsers.length == 2)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Avatar(
|
||||
mxContent: typingUsers.length == 2
|
||||
? typingUsers.last.avatarUrl
|
||||
: null,
|
||||
name: typingUsers.length == 2
|
||||
? typingUsers.last.calcDisplayname()
|
||||
: '+${typingUsers.length - 1}',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: topPadding),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(2),
|
||||
topRight: Radius.circular(AppConfig.borderRadius),
|
||||
bottomLeft: Radius.circular(AppConfig.borderRadius),
|
||||
bottomRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: typingUsers.isEmpty ? null : const _TypingDots(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ class ChatAccessSettingsPageView extends StatelessWidget {
|
|||
),
|
||||
body: MaxWidthBody(
|
||||
child: StreamBuilder<Object>(
|
||||
stream: room.onUpdate.stream,
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == controller.room.id),
|
||||
builder: (context, snapshot) {
|
||||
final canonicalAlias = room.canonicalAlias;
|
||||
final altAliases = room
|
||||
|
|
|
@ -36,7 +36,8 @@ class ChatDetailsView extends StatelessWidget {
|
|||
}
|
||||
|
||||
return StreamBuilder(
|
||||
stream: room.onUpdate.stream,
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
builder: (context, snapshot) {
|
||||
var members = room.getParticipants().toList()
|
||||
..sort((b, a) => a.powerLevel.compareTo(b.powerLevel));
|
||||
|
|
|
@ -82,7 +82,8 @@ class ChatEncryptionSettingsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: room.onUpdate.stream,
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == controller.room.id),
|
||||
builder: (context, snapshot) =>
|
||||
FutureBuilder<List<DeviceKeys>>(
|
||||
future: room.getUserDeviceKeys(),
|
||||
|
|
|
@ -64,7 +64,8 @@ class InvitationSelectionView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
StreamBuilder<Object>(
|
||||
stream: room.onUpdate.stream,
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
builder: (context, snapshot) {
|
||||
final participants =
|
||||
room.getParticipants().map((user) => user.id).toSet();
|
||||
|
|
|
@ -21,7 +21,8 @@ class MultipleEmotesSettingsView extends StatelessWidget {
|
|||
title: Text(L10n.of(context)!.emotePacks),
|
||||
),
|
||||
body: StreamBuilder(
|
||||
stream: room.onUpdate.stream,
|
||||
stream: room.client.onRoomState.stream
|
||||
.where((update) => update.roomId == room.id),
|
||||
builder: (context, snapshot) {
|
||||
final packStateEvents = room.states['im.ponies.room_emotes'];
|
||||
// we need to manually convert the map using Map.of, otherwise assigning null will throw a type error.
|
||||
|
|
|
@ -120,6 +120,7 @@ abstract class ClientManager {
|
|||
},
|
||||
nativeImplementations: nativeImplementations,
|
||||
customImageResizer: PlatformInfos.isMobile ? customImageResizer : null,
|
||||
defaultNetworkRequestTimeout: const Duration(minutes: 5),
|
||||
enableDehydratedDevices: true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ Future<DatabaseApi> flutterMatrixSdkDatabaseBuilder(Client client) async {
|
|||
),
|
||||
);
|
||||
|
||||
// Delete database file:
|
||||
if (database == null && !kIsWeb) {
|
||||
final dbFile = File(await _getDatabasePath(client.clientName));
|
||||
if (await dbFile.exists()) await dbFile.delete();
|
||||
}
|
||||
|
||||
try {
|
||||
// Send error notification:
|
||||
final l10n = lookupL10n(PlatformDispatcher.instance.locale);
|
||||
|
@ -61,9 +67,6 @@ Future<MatrixSdkDatabase> _constructDatabase(Client client) async {
|
|||
|
||||
final cipher = await getDatabaseCipher();
|
||||
|
||||
final databaseDirectory = PlatformInfos.isIOS || PlatformInfos.isMacOS
|
||||
? await getLibraryDirectory()
|
||||
: await getApplicationSupportDirectory();
|
||||
Directory? fileStorageLocation;
|
||||
try {
|
||||
fileStorageLocation = await getTemporaryDirectory();
|
||||
|
@ -73,7 +76,7 @@ Future<MatrixSdkDatabase> _constructDatabase(Client client) async {
|
|||
);
|
||||
}
|
||||
|
||||
final path = join(databaseDirectory.path, '${client.clientName}.sqlite');
|
||||
final path = await _getDatabasePath(client.clientName);
|
||||
|
||||
// fix dlopen for old Android
|
||||
await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions();
|
||||
|
@ -118,6 +121,14 @@ Future<MatrixSdkDatabase> _constructDatabase(Client client) async {
|
|||
);
|
||||
}
|
||||
|
||||
Future<String> _getDatabasePath(String clientName) async {
|
||||
final databaseDirectory = PlatformInfos.isIOS || PlatformInfos.isMacOS
|
||||
? await getLibraryDirectory()
|
||||
: await getApplicationSupportDirectory();
|
||||
|
||||
return join(databaseDirectory.path, '$clientName.sqlite');
|
||||
}
|
||||
|
||||
Future<void> _migrateLegacyLocation(
|
||||
String sqlFilePath,
|
||||
String clientName,
|
||||
|
|
|
@ -24,7 +24,7 @@ import macos_window_utils
|
|||
import package_info_plus
|
||||
import pasteboard
|
||||
import path_provider_foundation
|
||||
import record_macos
|
||||
import record_darwin
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sqflite
|
||||
|
@ -55,7 +55,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin"))
|
||||
RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
|
|
48
pubspec.lock
48
pubspec.lock
|
@ -1210,10 +1210,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: matrix
|
||||
sha256: "8610e6d207d6b667e4fe9e769d5b479db27aa1f80570880d3f171a5d3ff49d1a"
|
||||
sha256: b96f16ec227138a61d148f2812c4d558b2930edbb6cd05d03b3a41c4fffd2f47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.29.2"
|
||||
version: "0.29.7"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1618,50 +1618,58 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: record
|
||||
sha256: f703397f5a60d9b2b655b3acc94ba079b2d9a67dc0725bdb90ef2fee2441ebf7
|
||||
sha256: "113b368168c49c78902ab37c2b354dea30a0aec5bdeca434073826b6ea73eca1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.4"
|
||||
version: "5.0.5"
|
||||
record_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_android
|
||||
sha256: "0df98e05873b22b443309e289bf1eb3b5b9a60e7779134334e2073eb0763a992"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
record_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_darwin
|
||||
sha256: ee8cb1bb1712d7ce38140ecabe70e5c286c02f05296d66043bee865ace7eb1b9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
record_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_linux
|
||||
sha256: "348db92c4ec1b67b1b85d791381c8c99d7c6908de141e7c9edc20dad399b15ce"
|
||||
sha256: "7d0e70cd51635128fe9d37d89bafd6011d7cbba9af8dc323079ae60f23546aef"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1"
|
||||
record_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_macos
|
||||
sha256: d1d0199d1395f05e218207e8cacd03eb9dc9e256ddfe2cfcbbb90e8edea06057
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
version: "0.7.1"
|
||||
record_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_platform_interface
|
||||
sha256: "7a2d4ce7ac3752505157e416e4e0d666a54b1d5d8601701b7e7e5e30bec181b4"
|
||||
sha256: "3a4b56e94ecd2a0b2b43eb1fa6f94c5b8484334f5d38ef43959c4bf97fb374cf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "1.0.2"
|
||||
record_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_web
|
||||
sha256: "219ffb4ca59b4338117857db56d3ffadbde3169bcaf1136f5f4d4656f4a2372d"
|
||||
sha256: "24847cdbcf999f7a5762170792f622ac844858766becd0f2370ec8ae22f7526e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "1.0.5"
|
||||
record_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_windows
|
||||
sha256: "42d545155a26b20d74f5107648dbb3382dbbc84dc3f1adc767040359e57a1345"
|
||||
sha256: "39998b3ea7d8d28b04159d82220e6e5e32a7c357c6fb2794f5736beea272f6c3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
version: "1.0.2"
|
||||
remove_emoji:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -64,8 +64,8 @@ dependencies:
|
|||
keyboard_shortcuts: ^0.1.4
|
||||
latlong2: ^0.9.1
|
||||
linkify: ^5.0.0
|
||||
matrix: ^0.29.2
|
||||
native_imaging: ^0.1.0
|
||||
matrix: ^0.29.7
|
||||
native_imaging: ^0.1.1
|
||||
package_info_plus: ^6.0.0
|
||||
pasteboard: ^0.2.0
|
||||
path: ^1.9.0
|
||||
|
@ -76,7 +76,7 @@ dependencies:
|
|||
punycode: ^1.0.0
|
||||
qr_code_scanner: ^1.0.1
|
||||
receive_sharing_intent: 1.4.5 # Update needs more work
|
||||
record: 4.4.4 # Upgrade to 5 currently breaks playing on iOS
|
||||
record: ^5.0.5
|
||||
scroll_to_index: ^3.0.1
|
||||
share_plus: ^9.0.0
|
||||
shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401
|
||||
|
|
Loading…
Reference in a new issue