Skip to content

Commit f35aabb

Browse files
committed
msglist: Support retrieving failed outbox message content
1 parent b76304e commit f35aabb

13 files changed

+299
-23
lines changed

assets/l10n/app_en.arb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,10 @@
808808
"@messageIsMovedLabel": {
809809
"description": "Label for a moved message. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
810810
},
811+
"messageIsntSentLabel": "MESSAGE ISN'T SENT. CHECK YOUR CONNECTION.",
812+
"@messageIsntSentLabel": {
813+
"description": "Label for a message that isn't sent. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)"
814+
},
811815
"pollVoterNames": "({voterNames})",
812816
"@pollVoterNames": {
813817
"description": "The list of people who voted for a poll option, wrapped in parentheses.",

lib/generated/l10n/zulip_localizations.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,12 @@ abstract class ZulipLocalizations {
11901190
/// **'MOVED'**
11911191
String get messageIsMovedLabel;
11921192

1193+
/// Label for a message that isn't sent. (Use ALL CAPS for cased alphabets: Latin, Greek, Cyrillic, etc.)
1194+
///
1195+
/// In en, this message translates to:
1196+
/// **'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.'**
1197+
String get messageIsntSentLabel;
1198+
11931199
/// The list of people who voted for a poll option, wrapped in parentheses.
11941200
///
11951201
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
659659
@override
660660
String get messageIsMovedLabel => 'MOVED';
661661

662+
@override
663+
String get messageIsntSentLabel =>
664+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
665+
662666
@override
663667
String pollVoterNames(String voterNames) {
664668
return '($voterNames)';

lib/generated/l10n/zulip_localizations_en.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
659659
@override
660660
String get messageIsMovedLabel => 'MOVED';
661661

662+
@override
663+
String get messageIsntSentLabel =>
664+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
665+
662666
@override
663667
String pollVoterNames(String voterNames) {
664668
return '($voterNames)';

lib/generated/l10n/zulip_localizations_ja.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
659659
@override
660660
String get messageIsMovedLabel => 'MOVED';
661661

662+
@override
663+
String get messageIsntSentLabel =>
664+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
665+
662666
@override
663667
String pollVoterNames(String voterNames) {
664668
return '($voterNames)';

lib/generated/l10n/zulip_localizations_nb.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
659659
@override
660660
String get messageIsMovedLabel => 'MOVED';
661661

662+
@override
663+
String get messageIsntSentLabel =>
664+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
665+
662666
@override
663667
String pollVoterNames(String voterNames) {
664668
return '($voterNames)';

lib/generated/l10n/zulip_localizations_pl.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,10 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
668668
@override
669669
String get messageIsMovedLabel => 'PRZENIESIONO';
670670

671+
@override
672+
String get messageIsntSentLabel =>
673+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
674+
671675
@override
672676
String pollVoterNames(String voterNames) {
673677
return '($voterNames)';

lib/generated/l10n/zulip_localizations_ru.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,10 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
672672
@override
673673
String get messageIsMovedLabel => 'ПЕРЕМЕЩЕНО';
674674

675+
@override
676+
String get messageIsntSentLabel =>
677+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
678+
675679
@override
676680
String pollVoterNames(String voterNames) {
677681
return '($voterNames)';

lib/generated/l10n/zulip_localizations_sk.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,10 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
661661
@override
662662
String get messageIsMovedLabel => 'PRESUNUTÉ';
663663

664+
@override
665+
String get messageIsntSentLabel =>
666+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
667+
664668
@override
665669
String pollVoterNames(String voterNames) {
666670
return '($voterNames)';

lib/generated/l10n/zulip_localizations_uk.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,10 @@ class ZulipLocalizationsUk extends ZulipLocalizations {
671671
@override
672672
String get messageIsMovedLabel => 'ПЕРЕМІЩЕНО';
673673

674+
@override
675+
String get messageIsntSentLabel =>
676+
'MESSAGE ISN\'T SENT. CHECK YOUR CONNECTION.';
677+
674678
@override
675679
String pollVoterNames(String voterNames) {
676680
return '($voterNames)';

lib/model/message.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,8 @@ mixin _OutboxMessageStore on PerAccountStoreBase {
275275
void _handleMessageEventOutbox(MessageEvent event) {
276276
if (event.localMessageId != null) {
277277
final localMessageId = int.parse(event.localMessageId!, radix: 10);
278-
// The outbox message can be missing if the user removes it (to be
279-
// implemented in #1441) before the event arrives.
280-
// Nothing to do in that case.
278+
// The outbox message can be missing if the user removes it before the
279+
// event arrives. Nothing to do in that case.
281280
_outboxMessages.remove(localMessageId);
282281
_outboxMessageDebounceTimers.remove(localMessageId)?.cancel();
283282
_outboxMessageWaitPeriodTimers.remove(localMessageId)?.cancel();

lib/widgets/message_list.dart

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:intl/intl.dart' hide TextDirection;
55

66
import '../api/model/model.dart';
77
import '../generated/l10n/zulip_localizations.dart';
8+
import '../model/message.dart';
89
import '../model/message_list.dart';
910
import '../model/narrow.dart';
1011
import '../model/store.dart';
@@ -1490,22 +1491,106 @@ class OutboxMessageWithPossibleSender extends StatelessWidget {
14901491

14911492
final MessageListOutboxMessageItem item;
14921493

1494+
// TODO restore the topic too
1495+
void _handlePress(BuildContext context) {
1496+
final content = item.message.content.endsWith('\n')
1497+
? item.message.content : '${item.message.content}\n';
1498+
1499+
final composeBoxController =
1500+
MessageListPage.ancestorOf(context).composeBoxState!.controller;
1501+
composeBoxController.content.insertPadded(content);
1502+
if (!composeBoxController.contentFocusNode.hasFocus) {
1503+
composeBoxController.contentFocusNode.requestFocus();
1504+
}
1505+
1506+
final store = PerAccountStoreWidget.of(context);
1507+
assert(store.outboxMessages.containsKey(item.message.localMessageId));
1508+
store.removeOutboxMessage(item.message.localMessageId);
1509+
}
1510+
14931511
@override
14941512
Widget build(BuildContext context) {
1495-
final message = item.message;
1496-
return Padding(
1497-
padding: const EdgeInsets.symmetric(vertical: 4),
1498-
child: Column(children: [
1499-
if (item.showSender)
1500-
_SenderRow(message: message, showTimestamp: false),
1501-
Padding(
1502-
padding: const EdgeInsets.symmetric(horizontal: 16),
1503-
// This is adapated from [MessageContent].
1504-
// TODO(#576): Offer InheritedMessage ancestor once we are ready
1505-
// to support local echoing images and lightbox.
1506-
child: DefaultTextStyle(
1507-
style: ContentTheme.of(context).textStylePlainParagraph,
1508-
child: BlockContentList(nodes: item.content.nodes))),
1509-
]));
1513+
final GestureTapCallback? handleTap;
1514+
final double opacity;
1515+
switch (item.message.state) {
1516+
case OutboxMessageState.hidden:
1517+
assert(false,
1518+
'Hidden OutboxMessage messages should not appear in message lists');
1519+
handleTap = null;
1520+
opacity = 1.0;
1521+
1522+
case OutboxMessageState.waiting:
1523+
handleTap = null;
1524+
opacity = 1.0;
1525+
1526+
case OutboxMessageState.failed:
1527+
case OutboxMessageState.waitPeriodExpired:
1528+
final isComposeBoxOffered =
1529+
MessageListPage.ancestorOf(context).composeBoxState != null;
1530+
handleTap = isComposeBoxOffered ? () => _handlePress(context) : null;
1531+
opacity = 0.6;
1532+
}
1533+
1534+
return GestureDetector(
1535+
onTap: handleTap,
1536+
behavior: HitTestBehavior.opaque,
1537+
child: Padding(
1538+
padding: const EdgeInsets.symmetric(vertical: 4),
1539+
child: Column(children: [
1540+
if (item.showSender)
1541+
_SenderRow(message: item.message, showTimestamp: false),
1542+
Padding(
1543+
padding: const EdgeInsets.symmetric(horizontal: 16),
1544+
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch,
1545+
children: [
1546+
// This is adapated from [MessageContent].
1547+
// TODO(#576): Offer InheritedMessage ancestor once we are ready
1548+
// to support local echoing images and lightbox.
1549+
Opacity(opacity: opacity, child: DefaultTextStyle(
1550+
style: ContentTheme.of(context).textStylePlainParagraph,
1551+
child: BlockContentList(nodes: item.content.nodes))),
1552+
1553+
_OutboxMessageStatusRow(outboxMessageState: item.message.state),
1554+
])),
1555+
])));
1556+
}
1557+
}
1558+
1559+
class _OutboxMessageStatusRow extends StatelessWidget {
1560+
const _OutboxMessageStatusRow({required this.outboxMessageState});
1561+
1562+
final OutboxMessageState outboxMessageState;
1563+
1564+
@override
1565+
Widget build(BuildContext context) {
1566+
switch (outboxMessageState) {
1567+
case OutboxMessageState.hidden:
1568+
assert(false,
1569+
'Hidden OutboxMessage messages should not appear in message lists');
1570+
return SizedBox.shrink();
1571+
1572+
case OutboxMessageState.waiting:
1573+
final designVariables = DesignVariables.of(context);
1574+
// TODO instead place within outer padding; see Figma:
1575+
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=4021-7303&m=dev
1576+
return LinearProgressIndicator(
1577+
minHeight: 2,
1578+
color: designVariables.foreground.withFadedAlpha(0.5),
1579+
backgroundColor: designVariables.foreground.withFadedAlpha(0.2));
1580+
1581+
case OutboxMessageState.failed:
1582+
case OutboxMessageState.waitPeriodExpired:
1583+
final designVariables = DesignVariables.of(context);
1584+
final zulipLocalizations = ZulipLocalizations.of(context);
1585+
return Text(
1586+
zulipLocalizations.messageIsntSentLabel,
1587+
textAlign: TextAlign.end,
1588+
style: TextStyle(
1589+
color: designVariables.btnLabelAttLowIntDanger,
1590+
fontSize: 12,
1591+
height: 12 / 12,
1592+
letterSpacing: proportionalLetterSpacing(
1593+
context, 0.05, baseFontSize: 12)));
1594+
}
15101595
}
15111596
}

0 commit comments

Comments
 (0)