-
Notifications
You must be signed in to change notification settings - Fork 310
compose_box: Replace compose box with a banner in DMs with deactivated users #816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ import '../model/store.dart'; | |
import 'autocomplete.dart'; | ||
import 'dialog.dart'; | ||
import 'store.dart'; | ||
import 'theme.dart'; | ||
|
||
const double _inputVerticalPadding = 8; | ||
const double _sendButtonSize = 36; | ||
|
@@ -850,11 +851,13 @@ class _ComposeBoxLayout extends StatelessWidget { | |
required this.sendButton, | ||
required this.contentController, | ||
required this.contentFocusNode, | ||
this.blockingErrorBanner, | ||
}); | ||
|
||
final Widget? topicInput; | ||
final Widget contentInput; | ||
final Widget sendButton; | ||
final Widget? blockingErrorBanner; | ||
final ComposeContentController contentController; | ||
final FocusNode contentFocusNode; | ||
|
||
|
@@ -883,28 +886,30 @@ class _ComposeBoxLayout extends StatelessWidget { | |
minimum: const EdgeInsets.fromLTRB(8, 0, 8, 8), | ||
child: Padding( | ||
padding: const EdgeInsets.only(top: 8.0), | ||
child: Column(children: [ | ||
Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ | ||
Expanded( | ||
child: Theme( | ||
data: inputThemeData, | ||
child: Column(children: [ | ||
if (topicInput != null) topicInput!, | ||
if (topicInput != null) const SizedBox(height: 8), | ||
contentInput, | ||
]))), | ||
const SizedBox(width: 8), | ||
sendButton, | ||
]), | ||
Theme( | ||
data: themeData.copyWith( | ||
iconTheme: themeData.iconTheme.copyWith(color: colorScheme.onSurfaceVariant)), | ||
child: Row(children: [ | ||
_AttachFileButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
_AttachMediaButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
_AttachFromCameraButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
])), | ||
])))); } | ||
child: blockingErrorBanner != null | ||
? SizedBox(width: double.infinity, child: blockingErrorBanner) | ||
: Column(children: [ | ||
Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ | ||
Expanded( | ||
child: Theme( | ||
data: inputThemeData, | ||
child: Column(children: [ | ||
if (topicInput != null) topicInput!, | ||
if (topicInput != null) const SizedBox(height: 8), | ||
contentInput, | ||
]))), | ||
const SizedBox(width: 8), | ||
sendButton, | ||
]), | ||
Theme( | ||
data: themeData.copyWith( | ||
iconTheme: themeData.iconTheme.copyWith(color: colorScheme.onSurfaceVariant)), | ||
child: Row(children: [ | ||
_AttachFileButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
_AttachMediaButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
_AttachFromCameraButton(contentController: contentController, contentFocusNode: contentFocusNode), | ||
])), | ||
])))); } | ||
} | ||
|
||
abstract class ComposeBoxController<T extends StatefulWidget> extends State<T> { | ||
|
@@ -973,6 +978,27 @@ class _StreamComposeBoxState extends State<_StreamComposeBox> implements Compose | |
} | ||
} | ||
|
||
class _ErrorBanner extends StatelessWidget { | ||
Comment on lines
979
to
+981
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is separating a StatefulWidget from its corresponding State; move it above to not separate them. |
||
const _ErrorBanner({required this.label}); | ||
|
||
final String label; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final designVariables = DesignVariables.of(context); | ||
return Container( | ||
padding: const EdgeInsets.all(8), | ||
decoration: BoxDecoration( | ||
color: designVariables.errorBannerBackground, | ||
border: Border.all(color: designVariables.errorBannerBorder), | ||
borderRadius: BorderRadius.circular(5)), | ||
child: Text(label, | ||
style: TextStyle(fontSize: 18, color: designVariables.errorBannerLabel), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _FixedDestinationComposeBox extends StatefulWidget { | ||
const _FixedDestinationComposeBox({super.key, required this.narrow}); | ||
|
||
|
@@ -998,6 +1024,19 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox | |
super.dispose(); | ||
} | ||
|
||
Widget? _errorBanner(BuildContext context) { | ||
if (widget.narrow case DmNarrow(:final otherRecipientIds)) { | ||
final store = PerAccountStoreWidget.of(context); | ||
final hasDeactivatedUser = otherRecipientIds.any((id) => | ||
!(store.users[id]?.isActive ?? true)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Simplest fix is to just say A more complete fix would be to look first for any unknown users in the list, and in that case show a different message. Could be "You cannot send messages to unknown users." — "unknown user" is the term we use for these users in the UI and in the Help Center: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's the opposite. 🙂 If we don't know about a user, we assume it as an active one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahhh, I miscounted the |
||
if (hasDeactivatedUser) { | ||
return _ErrorBanner(label: ZulipLocalizations.of(context) | ||
.errorBannerDeactivatedDmLabel); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return _ComposeBoxLayout( | ||
|
@@ -1013,7 +1052,8 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox | |
topicController: null, | ||
contentController: _contentController, | ||
getDestination: () => widget.narrow.destination, | ||
)); | ||
), | ||
blockingErrorBanner: _errorBanner(context)); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, seems like a bug we weren't updating this before — good catch!
It looks like this PR is missing code to actually apply the update in
handleEvent
, though. So that should go in the same commit that adds this field to the event.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
… Ah I see, the story is that this event didn't have this field until FL 222, in server-8:

https://zulip.com/api/get-events#realm_user-update
Instead it would send RealmUserAddEvent or RealmUserRemoveEvent. And those we handled already.
Anyway, the same comment about applying the update still applies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for spotting. I was wondering why the tests for the previous revision were failing, and now I know why. When rebasing
store [nfc]: Replace else-if's with switch for handleEvent
, I forgot to include this part when resolving the conflicts. 😀