Skip to content

Commit e5f1043

Browse files
committed
msglist: Support retrieving failed outbox message content
1 parent a171020 commit e5f1043

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';
@@ -1521,22 +1522,106 @@ class OutboxMessageWithPossibleSender extends StatelessWidget {
15211522

15221523
final MessageListOutboxMessageItem item;
15231524

1525+
// TODO restore the topic too
1526+
void _handlePress(BuildContext context) {
1527+
final content = item.message.content.endsWith('\n')
1528+
? item.message.content : '${item.message.content}\n';
1529+
1530+
final composeBoxController =
1531+
MessageListPage.ancestorOf(context).composeBoxState!.controller;
1532+
composeBoxController.content.insertPadded(content);
1533+
if (!composeBoxController.contentFocusNode.hasFocus) {
1534+
composeBoxController.contentFocusNode.requestFocus();
1535+
}
1536+
1537+
final store = PerAccountStoreWidget.of(context);
1538+
assert(store.outboxMessages.containsKey(item.message.localMessageId));
1539+
store.removeOutboxMessage(item.message.localMessageId);
1540+
}
1541+
15241542
@override
15251543
Widget build(BuildContext context) {
1526-
final message = item.message;
1527-
return Padding(
1528-
padding: const EdgeInsets.symmetric(vertical: 4),
1529-
child: Column(children: [
1530-
if (item.showSender)
1531-
_SenderRow(message: message, showTimestamp: false),
1532-
Padding(
1533-
padding: const EdgeInsets.symmetric(horizontal: 16),
1534-
// This is adapated from [MessageContent].
1535-
// TODO(#576): Offer InheritedMessage ancestor once we are ready
1536-
// to support local echoing images and lightbox.
1537-
child: DefaultTextStyle(
1538-
style: ContentTheme.of(context).textStylePlainParagraph,
1539-
child: BlockContentList(nodes: item.content.nodes))),
1540-
]));
1544+
final GestureTapCallback? handleTap;
1545+
final double opacity;
1546+
switch (item.message.state) {
1547+
case OutboxMessageState.hidden:
1548+
assert(false,
1549+
'Hidden OutboxMessage messages should not appear in message lists');
1550+
handleTap = null;
1551+
opacity = 1.0;
1552+
1553+
case OutboxMessageState.waiting:
1554+
handleTap = null;
1555+
opacity = 1.0;
1556+
1557+
case OutboxMessageState.failed:
1558+
case OutboxMessageState.waitPeriodExpired:
1559+
final isComposeBoxOffered =
1560+
MessageListPage.ancestorOf(context).composeBoxState != null;
1561+
handleTap = isComposeBoxOffered ? () => _handlePress(context) : null;
1562+
opacity = 0.6;
1563+
}
1564+
1565+
return GestureDetector(
1566+
onTap: handleTap,
1567+
behavior: HitTestBehavior.opaque,
1568+
child: Padding(
1569+
padding: const EdgeInsets.symmetric(vertical: 4),
1570+
child: Column(children: [
1571+
if (item.showSender)
1572+
_SenderRow(message: item.message, showTimestamp: false),
1573+
Padding(
1574+
padding: const EdgeInsets.symmetric(horizontal: 16),
1575+
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch,
1576+
children: [
1577+
// This is adapated from [MessageContent].
1578+
// TODO(#576): Offer InheritedMessage ancestor once we are ready
1579+
// to support local echoing images and lightbox.
1580+
Opacity(opacity: opacity, child: DefaultTextStyle(
1581+
style: ContentTheme.of(context).textStylePlainParagraph,
1582+
child: BlockContentList(nodes: item.content.nodes))),
1583+
1584+
_OutboxMessageStatusRow(outboxMessageState: item.message.state),
1585+
])),
1586+
])));
1587+
}
1588+
}
1589+
1590+
class _OutboxMessageStatusRow extends StatelessWidget {
1591+
const _OutboxMessageStatusRow({required this.outboxMessageState});
1592+
1593+
final OutboxMessageState outboxMessageState;
1594+
1595+
@override
1596+
Widget build(BuildContext context) {
1597+
switch (outboxMessageState) {
1598+
case OutboxMessageState.hidden:
1599+
assert(false,
1600+
'Hidden OutboxMessage messages should not appear in message lists');
1601+
return SizedBox.shrink();
1602+
1603+
case OutboxMessageState.waiting:
1604+
final designVariables = DesignVariables.of(context);
1605+
// TODO instead place within outer padding; see Figma:
1606+
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=4021-7303&m=dev
1607+
return LinearProgressIndicator(
1608+
minHeight: 2,
1609+
color: designVariables.foreground.withFadedAlpha(0.5),
1610+
backgroundColor: designVariables.foreground.withFadedAlpha(0.2));
1611+
1612+
case OutboxMessageState.failed:
1613+
case OutboxMessageState.waitPeriodExpired:
1614+
final designVariables = DesignVariables.of(context);
1615+
final zulipLocalizations = ZulipLocalizations.of(context);
1616+
return Text(
1617+
zulipLocalizations.messageIsntSentLabel,
1618+
textAlign: TextAlign.end,
1619+
style: TextStyle(
1620+
color: designVariables.btnLabelAttLowIntDanger,
1621+
fontSize: 12,
1622+
height: 12 / 12,
1623+
letterSpacing: proportionalLetterSpacing(
1624+
context, 0.05, baseFontSize: 12)));
1625+
}
15411626
}
15421627
}

0 commit comments

Comments
 (0)