fixed bug that prevents sending messages if reply was triggered in a different chat
This commit is contained in:
@@ -4,16 +4,22 @@ import 'app_exception.dart';
|
|||||||
class TalkException extends AppException {
|
class TalkException extends AppException {
|
||||||
final TalkError source;
|
final TalkError source;
|
||||||
|
|
||||||
TalkException(this.source)
|
TalkException(this.source, {String? technicalDetails})
|
||||||
: super(
|
: super(
|
||||||
userMessage: _mapMessage(source),
|
userMessage: _mapMessage(source),
|
||||||
technicalDetails:
|
technicalDetails:
|
||||||
|
technicalDetails ??
|
||||||
'Talk ${source.status} (${source.code}): ${source.message}',
|
'Talk ${source.status} (${source.code}): ${source.message}',
|
||||||
allowRetry: source.code >= 500,
|
allowRetry: source.code >= 500,
|
||||||
);
|
);
|
||||||
|
|
||||||
static String _mapMessage(TalkError e) {
|
static String _mapMessage(TalkError e) {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
|
case 400:
|
||||||
|
return 'Talk hat diese Anfrage abgelehnt. Bitte lade den Chat neu und '
|
||||||
|
'versuche es erneut.';
|
||||||
|
case 413:
|
||||||
|
return 'Diese Nachricht ist zu lang.';
|
||||||
case 401:
|
case 401:
|
||||||
return 'Bitte melde dich erneut an, um auf Talk zuzugreifen.';
|
return 'Bitte melde dich erneut an, um auf Talk zuzugreifen.';
|
||||||
case 403:
|
case 403:
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import '../../api_params.dart';
|
|||||||
import '../../api_response.dart';
|
import '../../api_response.dart';
|
||||||
import '../../errors/network_exception.dart';
|
import '../../errors/network_exception.dart';
|
||||||
import '../../errors/parse_exception.dart';
|
import '../../errors/parse_exception.dart';
|
||||||
|
import '../../errors/talk_exception.dart';
|
||||||
import '../../http_errors.dart';
|
import '../../http_errors.dart';
|
||||||
import '../nextcloud_ocs.dart';
|
import '../nextcloud_ocs.dart';
|
||||||
|
import 'talk_error.dart';
|
||||||
|
|
||||||
abstract class TalkApi<T extends ApiResponse?> {
|
abstract class TalkApi<T extends ApiResponse?> {
|
||||||
String path;
|
String path;
|
||||||
@@ -49,6 +51,16 @@ abstract class TalkApi<T extends ApiResponse?> {
|
|||||||
// logs surface the cause instead of just the bare status code.
|
// logs surface the cause instead of just the bare status code.
|
||||||
final detail = httpErrorDetail('Talk $endpoint', data.body, status);
|
final detail = httpErrorDetail('Talk $endpoint', data.body, status);
|
||||||
log(detail);
|
log(detail);
|
||||||
|
// Talk's own codes need their own wording: a 400/412/413 here is a
|
||||||
|
// rejected request, not a struggling server, and "try again later"
|
||||||
|
// would send the user in circles. 401/403/404 keep the shared mapping
|
||||||
|
// so the auth handling stays in one place.
|
||||||
|
if (status == 400 || status == 412 || status == 413 || status == 429) {
|
||||||
|
throw TalkException(
|
||||||
|
TalkError('failure', status, ''),
|
||||||
|
technicalDetails: detail,
|
||||||
|
);
|
||||||
|
}
|
||||||
throwForStatus(status, detail);
|
throwForStatus(status, detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,13 @@ class ChatBloc
|
|||||||
_stopLongPoll();
|
_stopLongPoll();
|
||||||
add(
|
add(
|
||||||
Emit(
|
Emit(
|
||||||
|
// A reply reference belongs to the room it was picked in — carrying it
|
||||||
|
// into the next chat makes every send there fail on an unrelated
|
||||||
|
// parent id. ChatTextfield restores this room's draft reply after.
|
||||||
(s) => s.copyWith(
|
(s) => s.copyWith(
|
||||||
currentToken: token,
|
currentToken: token,
|
||||||
chatResponse: null,
|
chatResponse: null,
|
||||||
|
referenceMessageId: null,
|
||||||
hasMoreOld: true,
|
hasMoreOld: true,
|
||||||
isLoadingOlder: false,
|
isLoadingOlder: false,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../../../api/marianumcloud/talk/send_message/send_message_params.dart
|
|||||||
import '../../../../api/marianumcloud/talk/share_files_to_chat.dart';
|
import '../../../../api/marianumcloud/talk/share_files_to_chat.dart';
|
||||||
import '../../../../api/marianumcloud/webdav/webdav_api.dart';
|
import '../../../../api/marianumcloud/webdav/webdav_api.dart';
|
||||||
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
|
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
|
||||||
|
import '../../../../state/app/modules/chat/bloc/chat_state.dart';
|
||||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||||
import '../../../../widget/async_action_button.dart';
|
import '../../../../widget/async_action_button.dart';
|
||||||
import '../../../../widget/demo_restricted.dart';
|
import '../../../../widget/demo_restricted.dart';
|
||||||
@@ -216,11 +217,27 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
_setDraft(newText);
|
_setDraft(newText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reply target for the next send, or null when there is none. Requires the
|
||||||
|
/// referenced message to sit in this room's loaded history — the same
|
||||||
|
/// criterion the reply banner renders on, so what the user sees is what gets
|
||||||
|
/// sent. Talk rejects the whole message when the parent id is foreign to the
|
||||||
|
/// room, so a leftover reference would otherwise block the chat entirely.
|
||||||
|
String? _replyTargetFor(ChatState? chatState) {
|
||||||
|
final referenceId = chatState?.referenceMessageId;
|
||||||
|
if (referenceId == null) return null;
|
||||||
|
if (chatState!.currentToken != widget.sendToToken) return null;
|
||||||
|
final messages = chatState.chatResponse?.data;
|
||||||
|
if (messages == null || !messages.any((e) => e.id == referenceId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return referenceId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _sendMessage(ChatBloc chatBloc) async {
|
Future<void> _sendMessage(ChatBloc chatBloc) async {
|
||||||
if (_textBoxController.text.isEmpty) return;
|
if (_textBoxController.text.isEmpty) return;
|
||||||
if (guardDemoAction(context)) return;
|
if (guardDemoAction(context)) return;
|
||||||
final text = _textBoxController.text;
|
final text = _textBoxController.text;
|
||||||
final replyTo = chatBloc.state.data?.referenceMessageId?.toString();
|
final replyTo = _replyTargetFor(chatBloc.state.data);
|
||||||
final ownToken = widget.sendToToken;
|
final ownToken = widget.sendToToken;
|
||||||
setState(() => _sendError = null);
|
setState(() => _sendError = null);
|
||||||
await SendMessage(
|
await SendMessage(
|
||||||
|
|||||||
Reference in New Issue
Block a user