overlay message updates

This commit is contained in:
Gabby Gurdin 2024-04-09 09:37:48 -04:00
parent 2f04f52943
commit bad30b0c4c
8 changed files with 234 additions and 222 deletions

View file

@ -104,7 +104,7 @@ class ChatPageWithRoom extends StatefulWidget {
class ChatController extends State<ChatPageWithRoom>
with WidgetsBindingObserver {
// #Pangea
// #Pangea
final PangeaController pangeaController = MatrixState.pangeaController;
late Choreographer choreographer = Choreographer(pangeaController, this);
// Pangea#
@ -135,26 +135,32 @@ class ChatController extends State<ChatPageWithRoom>
// void onDragDone(DropDoneDetails details) async {
// setState(() => dragging = false);
// final bytesList = await showFutureLoadingDialog(
// if (details.files.isEmpty) return;
// final result = await showFutureLoadingDialog(
// context: context,
// future: () => Future.wait(
// details.files.map(
// (xfile) => xfile.readAsBytes(),
// ),
// ),
// future: () async {
// final clientConfig = await room.client.getConfig();
// final maxUploadSize = clientConfig.mUploadSize ?? 100 * 1024 * 1024;
// final matrixFiles = await Future.wait(
// details.files.map(
// (xfile) async {
// final length = await xfile.length();
// if (length > maxUploadSize) {
// throw FileTooBigMatrixException(length, maxUploadSize);
// }
// return MatrixFile(
// bytes: await xfile.readAsBytes(),
// name: xfile.name,
// mimeType: xfile.mimeType,
// ).detectFileType;
// },
// ),
// );
// return matrixFiles;
// },
// );
// if (bytesList.error != null) return;
// final matrixFiles = <MatrixFile>[];
// for (var i = 0; i < bytesList.result!.length; i++) {
// matrixFiles.add(
// MatrixFile(
// bytes: bytesList.result![i],
// name: details.files[i].name,
// ).detectFileType,
// );
// }
// if (matrixFiles.isEmpty) return;
// final matrixFiles = result.result;
// if (matrixFiles == null || matrixFiles.isEmpty) return;
// await showAdaptiveDialog(
// context: context,
@ -165,7 +171,6 @@ class ChatController extends State<ChatPageWithRoom>
// );
// }
// Pangea#
bool get canSaveSelectedEvent =>
selectedEvents.length == 1 &&
{
@ -178,7 +183,7 @@ class ChatController extends State<ChatPageWithRoom>
void saveSelectedEvent(context) => selectedEvents.single.saveFile(context);
final List<Event> selectedEvents = [];
List<Event> selectedEvents = [];
final Set<String> unfolded = {};
@ -186,14 +191,14 @@ class ChatController extends State<ChatPageWithRoom>
Event? editEvent;
bool scrolledUp = false;
bool _scrolledUp = false;
bool get showScrollDownButton =>
scrolledUp || timeline?.allowNewEvent == false;
_scrolledUp || timeline?.allowNewEvent == false;
bool get selectMode => selectedEvents.isNotEmpty;
final int loadHistoryCount = 100;
final int _loadHistoryCount = 100;
String pendingText = '';
@ -225,13 +230,13 @@ class ChatController extends State<ChatPageWithRoom>
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
// #Pangea
// void requestHistory() async {
// void requestHistory([_]) async {
Future<void> requestHistory() async {
if (timeline == null) return;
// Pangea#
if (!timeline!.canRequestHistory) return;
Logs().v('Requesting history...');
await timeline!.requestHistory(historyCount: loadHistoryCount);
await timeline!.requestHistory(historyCount: _loadHistoryCount);
}
void requestFuture() async {
@ -240,20 +245,20 @@ class ChatController extends State<ChatPageWithRoom>
if (!timeline.canRequestFuture) return;
Logs().v('Requesting future...');
final mostRecentEventId = timeline.events.first.eventId;
await timeline.requestFuture(historyCount: loadHistoryCount);
await timeline.requestFuture(historyCount: _loadHistoryCount);
setReadMarker(eventId: mostRecentEventId);
}
void updateScrollController() {
void _updateScrollController() {
if (!mounted) {
return;
}
if (!scrollController.hasClients) return;
if (timeline?.allowNewEvent == false ||
scrollController.position.pixels > 0 && scrolledUp == false) {
setState(() => scrolledUp = true);
} else if (scrollController.position.pixels <= 0 && scrolledUp == true) {
setState(() => scrolledUp = false);
scrollController.position.pixels > 0 && _scrolledUp == false) {
setState(() => _scrolledUp = true);
} else if (scrollController.position.pixels <= 0 && _scrolledUp == true) {
setState(() => _scrolledUp = false);
setReadMarker();
}
@ -263,7 +268,7 @@ class ChatController extends State<ChatPageWithRoom>
}
}
void loadDraft() async {
void _loadDraft() async {
final prefs = await SharedPreferences.getInstance();
final draft = widget.shareText ?? prefs.getString('draft_$roomId');
if (draft != null && draft.isNotEmpty) {
@ -277,15 +282,16 @@ class ChatController extends State<ChatPageWithRoom>
@override
void initState() {
scrollController.addListener(updateScrollController);
inputFocus.addListener(inputFocusListener);
loadDraft();
scrollController.addListener(_updateScrollController);
inputFocus.addListener(_inputFocusListener);
_loadDraft();
super.initState();
displayChatDetailsColumn = ValueNotifier(
_displayChatDetailsColumn = ValueNotifier(
Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
false,
);
sendingClient = Matrix.of(context).client;
WidgetsBinding.instance.addObserver(this);
// #Pangea
if (!mounted) return;
Future.delayed(const Duration(seconds: 1), () async {
@ -327,15 +333,14 @@ class ChatController extends State<ChatPageWithRoom>
},
);
// Pangea#
WidgetsBinding.instance.addObserver(this);
tryLoadTimeline();
_tryLoadTimeline();
if (kIsWeb) {
onFocusSub = html.window.onFocus.listen((_) => setReadMarker());
}
}
void tryLoadTimeline() async {
loadTimelineFuture = getTimeline();
void _tryLoadTimeline() async {
loadTimelineFuture = _getTimeline();
try {
await loadTimelineFuture;
final fullyRead = room.fullyRead;
@ -349,7 +354,7 @@ class ChatController extends State<ChatPageWithRoom>
return;
}
if (!mounted) return;
showScrollUpMaterialBanner(fullyRead);
_showScrollUpMaterialBanner(fullyRead);
} catch (e, s) {
ErrorReporter(context, 'Unable to load timeline').onErrorCallback(e, s);
rethrow;
@ -362,7 +367,7 @@ class ChatController extends State<ChatPageWithRoom>
scrollUpBannerEventId = null;
});
void showScrollUpMaterialBanner(String eventId) => setState(() {
void _showScrollUpMaterialBanner(String eventId) => setState(() {
scrollUpBannerEventId = eventId;
});
@ -395,7 +400,7 @@ class ChatController extends State<ChatPageWithRoom>
<Event>[];
// Pangea#
Future<void> getTimeline({
Future<void> _getTimeline({
String? eventContextId,
}) async {
await Matrix.of(context).client.roomsLoading;
@ -433,7 +438,7 @@ class ChatController extends State<ChatPageWithRoom>
);
if (!mounted) return;
if (e is TimeoutException || e is IOException) {
showScrollUpMaterialBanner(eventContextId!);
_showScrollUpMaterialBanner(eventContextId!);
}
}
timeline!.requestKeys(onlineKeyBackupOnly: false);
@ -462,11 +467,11 @@ class ChatController extends State<ChatPageWithRoom>
setReadMarker();
}
Future<void>? setReadMarkerFuture;
Future<void>? _setReadMarkerFuture;
void setReadMarker({String? eventId}) {
if (setReadMarkerFuture != null) return;
if (scrolledUp) return;
if (_setReadMarkerFuture != null) return;
if (_scrolledUp) return;
if (scrollUpBannerEventId != null) return;
if (eventId == null &&
!room.hasNewMessages &&
@ -486,13 +491,13 @@ class ChatController extends State<ChatPageWithRoom>
Logs().d('Set read marker...', eventId);
// ignore: unawaited_futures
setReadMarkerFuture = timeline
_setReadMarkerFuture = timeline
.setReadMarker(
eventId: eventId,
public: AppConfig.sendPublicReadReceipts,
)
.then((_) {
setReadMarkerFuture = null;
_setReadMarkerFuture = null;
});
if (eventId == null || eventId == timeline.room.lastEvent?.eventId) {
Matrix.of(context).backgroundPush?.cancelNotification(roomId);
@ -503,12 +508,12 @@ class ChatController extends State<ChatPageWithRoom>
void dispose() {
timeline?.cancelSubscriptions();
timeline = null;
inputFocus.removeListener(inputFocusListener);
inputFocus.removeListener(_inputFocusListener);
onFocusSub?.cancel();
//#Pangea
choreographer.stateListener.close();
choreographer.dispose();
//Pangea#
onFocusSub?.cancel();
super.dispose();
}
@ -528,7 +533,7 @@ class ChatController extends State<ChatPageWithRoom>
}
// then cancel the old timeline
// fixes bug with read reciepts and quick switching
loadTimelineFuture = getTimeline(eventContextId: room.fullyRead).onError(
loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError(
ErrorReporter(
context,
'Unable to load timeline after changing sending Client',
@ -558,7 +563,7 @@ class ChatController extends State<ChatPageWithRoom>
}) async {
// Pangea#
if (sendController.text.trim().isEmpty) return;
storeInputTimeoutTimer?.cancel();
_storeInputTimeoutTimer?.cancel();
final prefs = await SharedPreferences.getInstance();
prefs.remove('draft_$roomId');
var parseCommands = true;
@ -782,7 +787,6 @@ class ChatController extends State<ChatPageWithRoom>
// #Pangea
// if (await Record().hasPermission() == false) return;
// Pangea#
final result = await showDialog<RecordingResult>(
context: context,
barrierDismissible: false,
@ -828,11 +832,6 @@ class ChatController extends State<ChatPageWithRoom>
}
void emojiPickerAction() {
// #Pangea
if (choreographer.itController.isOpen) {
return;
}
// Pangea#
if (showEmojiPicker) {
inputFocus.requestFocus();
} else {
@ -842,7 +841,7 @@ class ChatController extends State<ChatPageWithRoom>
setState(() => showEmojiPicker = !showEmojiPicker);
}
void inputFocusListener() {
void _inputFocusListener() {
if (showEmojiPicker && inputFocus.hasFocus) {
emojiPickerType = EmojiPickerType.keyboard;
setState(() => showEmojiPicker = false);
@ -856,7 +855,7 @@ class ChatController extends State<ChatPageWithRoom>
);
}
String getSelectedEventString() {
String _getSelectedEventString() {
var copyString = '';
if (selectedEvents.length == 1) {
return selectedEvents.first
@ -874,7 +873,7 @@ class ChatController extends State<ChatPageWithRoom>
}
void copyEventsAction() {
Clipboard.setData(ClipboardData(text: getSelectedEventString()));
Clipboard.setData(ClipboardData(text: _getSelectedEventString()));
setState(() {
showEmojiPicker = false;
selectedEvents.clear();
@ -1065,7 +1064,7 @@ class ChatController extends State<ChatPageWithRoom>
} else {
Matrix.of(context).shareContent = {
'msgtype': 'm.text',
'body': getSelectedEventString(),
'body': _getSelectedEventString(),
};
}
setState(() => selectedEvents.clear());
@ -1099,8 +1098,8 @@ class ChatController extends State<ChatPageWithRoom>
if (eventIndex == -1) {
setState(() {
timeline = null;
scrolledUp = false;
loadTimelineFuture = getTimeline(eventContextId: eventId).onError(
_scrolledUp = false;
loadTimelineFuture = _getTimeline(eventContextId: eventId).onError(
ErrorReporter(context, 'Unable to load timeline after scroll to ID')
.onErrorCallback,
);
@ -1118,15 +1117,15 @@ class ChatController extends State<ChatPageWithRoom>
eventIndex,
preferPosition: AutoScrollPosition.middle,
);
updateScrollController();
_updateScrollController();
}
void scrollDown() async {
if (!timeline!.allowNewEvent) {
setState(() {
timeline = null;
scrolledUp = false;
loadTimelineFuture = getTimeline().onError(
_scrolledUp = false;
loadTimelineFuture = _getTimeline().onError(
ErrorReporter(context, 'Unable to load timeline after scroll down')
.onErrorCallback,
);
@ -1152,7 +1151,7 @@ class ChatController extends State<ChatPageWithRoom>
setState(() => showEmojiPicker = false);
if (emoji == null) return;
// make sure we don't send the same emoji twice
if (allReactionEvents.any(
if (_allReactionEvents.any(
(e) => e.content.tryGetMap('m.relates_to')?['key'] == emoji.emoji,
)) {
return;
@ -1176,7 +1175,7 @@ class ChatController extends State<ChatPageWithRoom>
);
}
late Iterable<Event> allReactionEvents;
late Iterable<Event> _allReactionEvents;
void emojiPickerBackspace() {
switch (emojiPickerType) {
@ -1194,7 +1193,7 @@ class ChatController extends State<ChatPageWithRoom>
}
void pickEmojiReactionAction(Iterable<Event> allReactionEvents) async {
allReactionEvents = allReactionEvents;
_allReactionEvents = allReactionEvents;
emojiPickerType = EmojiPickerType.reaction;
setState(() => showEmojiPicker = true);
}
@ -1213,7 +1212,7 @@ class ChatController extends State<ChatPageWithRoom>
void clearSelectedEvents() => setState(() {
selectedEvents.clear();
showEmojiPicker = false;
//#Pangea
//#Pangea
choreographer.messageOptions.resetSelectedDisplayLang();
//Pangea#
});
@ -1284,7 +1283,6 @@ class ChatController extends State<ChatPageWithRoom>
if (choreographer.itController.isOpen) {
return;
}
// Pangea#
if (!event.redacted) {
if (selectedEvents.contains(event)) {
@ -1387,8 +1385,8 @@ class ChatController extends State<ChatPageWithRoom>
);
}
Timer? storeInputTimeoutTimer;
static const Duration storeInputTimeout = Duration(milliseconds: 500);
Timer? _storeInputTimeoutTimer;
static const Duration _storeInputTimeout = Duration(milliseconds: 500);
void onInputBarChanged(String text) {
if (_inputTextIsEmpty != text.isEmpty) {
@ -1397,8 +1395,8 @@ class ChatController extends State<ChatPageWithRoom>
});
}
storeInputTimeoutTimer?.cancel();
storeInputTimeoutTimer = Timer(storeInputTimeout, () async {
_storeInputTimeoutTimer?.cancel();
_storeInputTimeoutTimer = Timer(_storeInputTimeout, () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('draft_$roomId', text);
});
@ -1518,10 +1516,12 @@ class ChatController extends State<ChatPageWithRoom>
bool? lastState;
bool get isRowScrollable {
if (availableSpace == null || inputRowSize == null) {
lastState = false;
Future.delayed(Duration.zero, () {
setState(() {});
});
if (lastState == null) {
lastState = false;
Future.delayed(Duration.zero, () {
setState(() {});
});
}
return false;
}
const double offSetValue = 10;
@ -1540,39 +1540,45 @@ class ChatController extends State<ChatPageWithRoom>
return currentState;
}
final Map<String, PangeaMessageEvent> pangeaMessageEvents = {};
final Map<String, ToolbarDisplayController> toolbarDisplayControllers = {};
final Map<String, PangeaMessageEvent> _pangeaMessageEvents = {};
final Map<String, ToolbarDisplayController> _toolbarDisplayControllers = {};
void setPangeaMessageEvent(String eventId) {
final Event? event = timeline!.events.firstWhereOrNull(
(e) => e.eventId == eventId,
);
if (event == null || timeline == null) return;
pangeaMessageEvents[eventId] = PangeaMessageEvent(
_pangeaMessageEvents[eventId] = PangeaMessageEvent(
event: event,
timeline: timeline!,
ownMessage: event.senderId == room.client.userID,
);
}
void setToolbarDisplayController(String eventId) {
void setToolbarDisplayController(
String eventId, {
Event? nextEvent,
Event? previousEvent,
}) {
final Event? event = timeline!.events.firstWhereOrNull(
(e) => e.eventId == eventId,
);
if (event == null || timeline == null) return;
if (pangeaMessageEvents[eventId] == null) {
if (_pangeaMessageEvents[eventId] == null) {
setPangeaMessageEvent(eventId);
if (pangeaMessageEvents[eventId] == null) return;
if (_pangeaMessageEvents[eventId] == null) return;
}
try {
toolbarDisplayControllers[eventId] = ToolbarDisplayController(
_toolbarDisplayControllers[eventId] = ToolbarDisplayController(
targetId: event.eventId,
pangeaMessageEvent: pangeaMessageEvents[eventId]!,
pangeaMessageEvent: _pangeaMessageEvents[eventId]!,
immersionMode: choreographer.immersionMode,
controller: this,
nextEvent: nextEvent,
previousEvent: previousEvent,
);
toolbarDisplayControllers[eventId]!.setToolbar();
_toolbarDisplayControllers[eventId]!.setToolbar();
} catch (e, s) {
ErrorHandler.logError(
e: e,
@ -1581,35 +1587,43 @@ class ChatController extends State<ChatPageWithRoom>
data: {
"eventId": eventId,
"event": event.toJson(),
"pangeaMessageEvent": pangeaMessageEvents[eventId]?.toString(),
"pangeaMessageEvent": _pangeaMessageEvents[eventId]?.toString(),
},
);
}
}
PangeaMessageEvent? getPangeaMessageEvent(String eventId) {
if (pangeaMessageEvents[eventId] == null) {
if (_pangeaMessageEvents[eventId] == null) {
setPangeaMessageEvent(eventId);
}
return pangeaMessageEvents[eventId];
return _pangeaMessageEvents[eventId];
}
ToolbarDisplayController? getToolbarDisplayController(String eventId) {
if (toolbarDisplayControllers[eventId] == null) {
setToolbarDisplayController(eventId);
ToolbarDisplayController? getToolbarDisplayController(
String eventId, {
Event? nextEvent,
Event? previousEvent,
}) {
if (_toolbarDisplayControllers[eventId] == null) {
setToolbarDisplayController(
eventId,
nextEvent: nextEvent,
previousEvent: previousEvent,
);
}
return toolbarDisplayControllers[eventId];
return _toolbarDisplayControllers[eventId];
}
// Pangea#
late final ValueNotifier<bool> displayChatDetailsColumn;
late final ValueNotifier<bool> _displayChatDetailsColumn;
void toggleDisplayChatDetailsColumn() async {
await Matrix.of(context).store.setBool(
SettingKeys.displayChatDetailsColumn,
!displayChatDetailsColumn.value,
!_displayChatDetailsColumn.value,
);
displayChatDetailsColumn.value = !displayChatDetailsColumn.value;
_displayChatDetailsColumn.value = !_displayChatDetailsColumn.value;
}
@override
@ -1622,7 +1636,7 @@ class ChatController extends State<ChatPageWithRoom>
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: ValueListenableBuilder(
valueListenable: displayChatDetailsColumn,
valueListenable: _displayChatDetailsColumn,
builder: (context, displayChatDetailsColumn, _) {
if (!FluffyThemes.isThreeColumnMode(context) ||
room.membership != Membership.join ||

View file

@ -28,7 +28,6 @@ class ChatEventList extends StatelessWidget {
final events = controller.timeline!.events
.where((event) => event.isVisibleInGui)
.toList();
final animateInEventIndex = controller.animateInEventIndex;
// create a map of eventId --> index to greatly improve performance of
@ -98,8 +97,12 @@ class ChatEventList extends StatelessWidget {
if (controller.timeline!.canRequestHistory) {
return Builder(
builder: (context) {
// #Pangea
WidgetsBinding.instance
.addPostFrameCallback((_) => controller.requestHistory);
// WidgetsBinding.instance
// .addPostFrameCallback(controller.requestHistory);
// Pangea#
return Center(
child: IconButton(
onPressed: controller.requestHistory,
@ -151,8 +154,8 @@ class ChatEventList extends StatelessWidget {
onSelect: controller.onSelectMessage,
scrollToEventId: (String eventId) =>
controller.scrollToEventId(eventId),
// #Pangea
longPressSelect: controller.selectedEvents.isNotEmpty,
// #Pangea
selectedDisplayLang:
controller.choreographer.messageOptions.selectedDisplayLang,
immersionMode: controller.choreographer.immersionMode,

View file

@ -116,12 +116,13 @@ class ChatInputRow extends StatelessWidget {
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
height: height,
//#Pangea
// width: controller.sendController.text.isEmpty ? 56 : 0,
// #Pangea
// width:
// controller.sendController.text.isEmpty ? height : 0,
width: controller.sendController.text.isEmpty &&
controller.pangeaController.permissionsController
.showChatInputAddButton(controller.roomId)
? height
? 56
: 0,
//Pangea#
alignment: Alignment.center,
@ -164,8 +165,7 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0),
),
),
//#Pangea
// #Pangea
// if (PlatformInfos.isMobile)
if (PlatformInfos.isMobile &&
controller.pangeaController.permissionsController
@ -191,15 +191,15 @@ class ChatInputRow extends StatelessWidget {
//Pangea#
PopupMenuItem<String>(
value: 'camera-video',
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.videocam_outlined),
),
title: Text(L10n.of(context)!.openVideoCamera),
contentPadding: const EdgeInsets.all(0),
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.videocam_outlined),
),
title: Text(L10n.of(context)!.openVideoCamera),
contentPadding: const EdgeInsets.all(0),
),
),
//#Pangea
// if (PlatformInfos.isMobile)
@ -219,22 +219,6 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0),
),
),
if (controller.room
.getImagePacks(ImagePackUsage.sticker)
.isNotEmpty)
PopupMenuItem<String>(
value: 'sticker',
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
child: Icon(Icons.emoji_emotions_outlined),
),
title: Text(L10n.of(context)!.sendSticker),
contentPadding: const EdgeInsets.all(0),
),
),
],
),
),
@ -269,7 +253,7 @@ class ChatInputRow extends StatelessWidget {
child: Icon(
controller.showEmojiPicker
? Icons.keyboard
: Icons.emoji_emotions_outlined,
: Icons.add_reaction_outlined,
key: ValueKey(controller.showEmojiPicker),
),
),
@ -282,14 +266,15 @@ class ChatInputRow extends StatelessWidget {
// Matrix.of(context).hasComplexBundles &&
// Matrix.of(context).currentBundle!.length > 1)
// Container(
// height: 56,
// width: height,
// height: height,
// alignment: Alignment.center,
// child: _ChatAccountPicker(controller),
// ),
// Pangea#
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
padding: const EdgeInsets.symmetric(vertical: 0.0),
child: InputBar(
room: controller.room,
minLines: 1,
@ -312,7 +297,7 @@ class ChatInputRow extends StatelessWidget {
focusNode: controller.inputFocus,
controller: controller.sendController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
contentPadding: const EdgeInsets.only(
left: 6.0,
right: 6.0,
bottom: 6.0,
@ -323,61 +308,51 @@ class ChatInputRow extends StatelessWidget {
border: InputBorder.none,
enabledBorder: InputBorder.none,
filled: false,
),
),
onChanged: controller.onInputBarChanged,
),
),
),
if (PlatformInfos.platformCanRecord &&
controller.sendController.text.isEmpty)
Container(
height: height,
width: height,
alignment: Alignment.center,
child: PlatformInfos.platformCanRecord &&
controller.sendController.text.isEmpty
? FloatingActionButton.small(
tooltip: L10n.of(context)!.voiceMessage,
onPressed: controller.voiceMessageAction,
elevation: 0,
heroTag: null,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(height),
),
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
child: const Icon(Icons.mic_none_outlined),
)
: FloatingActionButton.small(
tooltip: L10n.of(context)!.send,
onPressed: controller.send,
elevation: 0,
heroTag: null,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(height),
),
backgroundColor:
Theme.of(context).colorScheme.onPrimaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
child: const Icon(Icons.send_outlined),
Container(
height: height,
width: height,
alignment: Alignment.center,
child: PlatformInfos.platformCanRecord &&
controller.sendController.text.isEmpty
? FloatingActionButton.small(
tooltip: L10n.of(context)!.voiceMessage,
onPressed: controller.voiceMessageAction,
elevation: 0,
heroTag: null,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(height),
),
),
if (!PlatformInfos.isMobile ||
controller.sendController.text.isNotEmpty)
// #Pangea
ChoreographerSendButton(controller: controller),
// Container(
// height: 56,
// alignment: Alignment.center,
// child: IconButton(
// icon: const Icon(Icons.send_outlined),
// onPressed: controller.send,
// tooltip: L10n.of(context)!.send,
// ),
// ),
// Pangea#
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
child: const Icon(Icons.mic_none_outlined),
)
:
// #Pangea
ChoreographerSendButton(controller: controller),
// FloatingActionButton.small(
// tooltip: L10n.of(context)!.send,
// onPressed: controller.send,
// elevation: 0,
// heroTag: null,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(height),
// ),
// backgroundColor: Theme.of(context)
// .colorScheme
// .onPrimaryContainer,
// foregroundColor:
// Theme.of(context).colorScheme.onPrimary,
// child: const Icon(Icons.send_outlined),
// ),
// Pangea#
),
],
),
],

View file

@ -1,7 +1,4 @@
import 'package:badges/badges.dart';
// #Pangea
// import 'package:desktop_drop/desktop_drop.dart';
// Pangea#
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
@ -122,7 +119,8 @@ class ChatView extends StatelessWidget {
ChatSettingsPopupMenu(controller.room, !controller.room.isDirectChat),
];
}
// } else if (!controller.room.isArchived) {
// else if (!controller.room.isArchived) {
// return [
// if (Matrix.of(context).voipPlugin != null &&
// controller.room.isDirectChat)
@ -200,7 +198,7 @@ class ChatView extends StatelessWidget {
// #Pangea
&&
!r.isAnalyticsRoom,
// Pangea#
// Pangea#,
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()),
),
@ -252,6 +250,9 @@ class ChatView extends StatelessWidget {
),
),
),
// #Pangea
// floatingActionButton: controller.showScrollDownButton &&
// controller.selectedEvents.isEmpty
floatingActionButton: controller.selectedEvents.isEmpty
? (controller.showScrollDownButton
// Pangea#
@ -427,7 +428,6 @@ class ChatView extends StatelessWidget {
// Pangea#
],
),
// ),
);
},
),

View file

@ -159,7 +159,11 @@ class Message extends StatelessWidget {
ToolbarDisplayController? toolbarController;
if (event.messageType == MessageTypes.Text ||
event.messageType == MessageTypes.Notice) {
toolbarController = controller.getToolbarDisplayController(event.eventId);
toolbarController = controller.getToolbarDisplayController(
event.eventId,
nextEvent: nextEvent,
previousEvent: previousEvent,
);
}
// Pangea#

View file

@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:fluffychat/pangea/constants/class_default_values.dart';
import 'package:fluffychat/pangea/extensions/client_extension.dart';
@ -7,7 +5,6 @@ import 'package:fluffychat/pangea/utils/class_code.dart';
import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart';
import 'package:fluffychat/pangea/utils/logout.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -113,17 +110,6 @@ class ClientChooserButton extends StatelessWidget {
],
),
),
PopupMenuItem(
value: SettingsAction.findAClass,
enabled: false,
child: Row(
children: [
const Icon(Icons.class_outlined),
const SizedBox(width: 18),
Expanded(child: Text(L10n.of(context)!.findAClass)),
],
),
),
if (controller.pangeaController.permissionsController.isUser18())
PopupMenuItem(
value: SettingsAction.findAConversationPartner,
@ -421,9 +407,6 @@ class ClientChooserButton extends StatelessWidget {
case SettingsAction.myAnalytics:
context.go('/rooms/mylearning');
break;
case SettingsAction.findAClass:
debugger(when: kDebugMode, message: "left to implement");
break;
case SettingsAction.logout:
pLogoutAction(context);
break;
@ -514,7 +497,6 @@ enum SettingsAction {
joinWithClassCode,
classAnalytics,
myAnalytics,
findAClass,
findAConversationPartner,
logout,
newClass,

View file

@ -26,6 +26,8 @@ class ToolbarDisplayController {
final bool immersionMode;
final ChatController controller;
final FocusNode focusNode = FocusNode();
Event? nextEvent;
Event? previousEvent;
MessageToolbar? toolbar;
String? overlayId;
@ -38,6 +40,8 @@ class ToolbarDisplayController {
required this.targetId,
required this.immersionMode,
required this.controller,
this.nextEvent,
this.previousEvent,
});
void setToolbar() {
@ -86,6 +90,8 @@ class ToolbarDisplayController {
ownMessage: pangeaMessageEvent.ownMessage,
toolbarController: this,
width: messageWidth,
nextEvent: nextEvent,
previousEvent: previousEvent,
),
],
);

View file

@ -11,6 +11,8 @@ import '../../../config/app_config.dart';
class OverlayMessage extends StatelessWidget {
final Event event;
final Event? nextEvent;
final Event? previousEvent;
final bool selected;
final Timeline timeline;
// #Pangea
@ -24,6 +26,8 @@ class OverlayMessage extends StatelessWidget {
const OverlayMessage(
this.event, {
this.nextEvent,
this.previousEvent,
this.selected = false,
required this.timeline,
// #Pangea
@ -44,19 +48,43 @@ class OverlayMessage extends StatelessWidget {
var color = Theme.of(context).colorScheme.surfaceVariant;
final textColor = ownMessage
? Theme.of(context).colorScheme.onPrimaryContainer
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onBackground;
const hardCorner = Radius.circular(4);
final displayTime = event.type == EventTypes.RoomCreate ||
nextEvent == null ||
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
final nextEventSameSender = nextEvent != null &&
{
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
}.contains(nextEvent!.type) &&
nextEvent!.senderId == event.senderId &&
!displayTime;
final previousEventSameSender = previousEvent != null &&
{
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
}.contains(previousEvent!.type) &&
previousEvent!.senderId == event.senderId &&
previousEvent!.originServerTs.sameEnvironment(event.originServerTs);
const roundedCorner = Radius.circular(AppConfig.borderRadius);
final borderRadius = BorderRadius.only(
topLeft: !ownMessage
? const Radius.circular(4)
: const Radius.circular(AppConfig.borderRadius),
topRight: const Radius.circular(AppConfig.borderRadius),
bottomLeft: const Radius.circular(AppConfig.borderRadius),
bottomRight: ownMessage
? const Radius.circular(4)
: const Radius.circular(AppConfig.borderRadius),
topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner,
topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner,
bottomLeft:
!ownMessage && previousEventSameSender ? hardCorner : roundedCorner,
bottomRight:
ownMessage && previousEventSameSender ? hardCorner : roundedCorner,
);
final noBubble = {
MessageTypes.Video,
MessageTypes.Image,
@ -69,7 +97,7 @@ class OverlayMessage extends StatelessWidget {
}.contains(event.messageType);
if (ownMessage) {
color = Theme.of(context).colorScheme.primaryContainer;
color = Theme.of(context).colorScheme.primary;
}
// #Pangea