overlay message updates
This commit is contained in:
parent
2f04f52943
commit
bad30b0c4c
8 changed files with 234 additions and 222 deletions
|
|
@ -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 ||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
],
|
||||
),
|
||||
// ),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue