Skip to content

Commit

Permalink
compose_box: Disable the compose box in DMs with deactivated users
Browse files Browse the repository at this point in the history
Fixes: #675
  • Loading branch information
sm-sayedi committed Jul 18, 2024
1 parent a038e72 commit 3c48c68
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 16 deletions.
4 changes: 4 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@
"user": {"type": "String", "example": "channel name"}
}
},
"composeBoxDeactivatedDmContentHint": "You cannot send messages to deactivated users.",
"@composeBoxDeactivatedDmContentHint": {
"description": "Hint text for content input when sending a message to one or multiple deactivated persons."
},
"composeBoxGroupDmContentHint": "Message group",
"@composeBoxGroupDmContentHint": {
"description": "Hint text for content input when sending a message to a group."
Expand Down
90 changes: 74 additions & 16 deletions lib/widgets/compose_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,18 @@ class _ContentInput extends StatelessWidget {
required this.controller,
required this.focusNode,
required this.hintText,
this.enabled = true,
});

final Narrow narrow;
final ComposeContentController controller;
final FocusNode focusNode;
final String hintText;
final bool enabled;

@override
Widget build(BuildContext context) {
ColorScheme colorScheme = Theme.of(context).colorScheme;

return InputDecorator(
decoration: const InputDecoration(),
child: ConstrainedBox(
Expand All @@ -303,6 +304,7 @@ class _ContentInput extends StatelessWidget {
decoration: InputDecoration.collapsed(hintText: hintText),
maxLines: null,
textCapitalization: TextCapitalization.sentences,
enabled: enabled,
);
}),
));
Expand Down Expand Up @@ -377,32 +379,37 @@ class _FixedDestinationContentInput extends StatelessWidget {
required this.narrow,
required this.controller,
required this.focusNode,
required this.enabled,
});

final SendableNarrow narrow;
final ComposeContentController controller;
final FocusNode focusNode;
final bool enabled;

String _hintText(BuildContext context) {
final zulipLocalizations = ZulipLocalizations.of(context);
switch (narrow) {
case TopicNarrow(:final streamId, :final topic):
switch ((narrow, enabled)) {
case (TopicNarrow(:final streamId, :final topic), _):
final store = PerAccountStoreWidget.of(context);
final streamName = store.streams[streamId]?.name
?? zulipLocalizations.composeBoxUnknownChannelName;
return zulipLocalizations.composeBoxChannelContentHint(streamName, topic);

case DmNarrow(otherRecipientIds: []): // The self-1:1 thread.
case (DmNarrow(otherRecipientIds: []), _): // The self-1:1 thread.
return zulipLocalizations.composeBoxSelfDmContentHint;

case DmNarrow(otherRecipientIds: [final otherUserId]):
case (DmNarrow(otherRecipientIds: [final otherUserId]), true):
final store = PerAccountStoreWidget.of(context);
final fullName = store.users[otherUserId]?.fullName;
if (fullName == null) return zulipLocalizations.composeBoxGenericContentHint;
return zulipLocalizations.composeBoxDmContentHint(fullName);

case DmNarrow(): // A group DM thread.
case (DmNarrow(), true): // A group DM thread.
return zulipLocalizations.composeBoxGroupDmContentHint;

case (DmNarrow(), false):
return zulipLocalizations.composeBoxDeactivatedDmContentHint;
}
}

Expand All @@ -412,7 +419,8 @@ class _FixedDestinationContentInput extends StatelessWidget {
narrow: narrow,
controller: controller,
focusNode: focusNode,
hintText: _hintText(context));
hintText: _hintText(context),
enabled: enabled);
}
}

Expand Down Expand Up @@ -492,10 +500,15 @@ Future<void> _uploadFiles({
}

abstract class _AttachUploadsButton extends StatelessWidget {
const _AttachUploadsButton({required this.contentController, required this.contentFocusNode});
const _AttachUploadsButton({
required this.contentController,
required this.contentFocusNode,
required this.enabled,
});

final ComposeContentController contentController;
final FocusNode contentFocusNode;
final bool enabled;

IconData get icon;
String tooltip(ZulipLocalizations zulipLocalizations);
Expand Down Expand Up @@ -534,7 +547,7 @@ abstract class _AttachUploadsButton extends StatelessWidget {
return IconButton(
icon: Icon(icon),
tooltip: tooltip(zulipLocalizations),
onPressed: () => _handlePress(context));
onPressed: enabled ? () => _handlePress(context) : null);
}
}

Expand Down Expand Up @@ -578,7 +591,11 @@ Future<Iterable<_File>> _getFilePickerFiles(BuildContext context, FileType type)
}

class _AttachFileButton extends _AttachUploadsButton {
const _AttachFileButton({required super.contentController, required super.contentFocusNode});
const _AttachFileButton({
required super.contentController,
required super.contentFocusNode,
required super.enabled,
});

@override
IconData get icon => Icons.attach_file;
Expand All @@ -594,7 +611,11 @@ class _AttachFileButton extends _AttachUploadsButton {
}

class _AttachMediaButton extends _AttachUploadsButton {
const _AttachMediaButton({required super.contentController, required super.contentFocusNode});
const _AttachMediaButton({
required super.contentController,
required super.contentFocusNode,
required super.enabled,
});

@override
IconData get icon => Icons.image;
Expand All @@ -611,7 +632,11 @@ class _AttachMediaButton extends _AttachUploadsButton {
}

class _AttachFromCameraButton extends _AttachUploadsButton {
const _AttachFromCameraButton({required super.contentController, required super.contentFocusNode});
const _AttachFromCameraButton({
required super.contentController,
required super.contentFocusNode,
required super.enabled,
});

@override
IconData get icon => Icons.camera_alt;
Expand Down Expand Up @@ -667,11 +692,13 @@ class _SendButton extends StatefulWidget {
required this.topicController,
required this.contentController,
required this.getDestination,
this.enabled = true,
});

final ComposeTopicController? topicController;
final ComposeContentController contentController;
final MessageDestination Function() getDestination;
final bool enabled;

@override
State<_SendButton> createState() => _SendButtonState();
Expand Down Expand Up @@ -774,7 +801,7 @@ class _SendButtonState extends State<_SendButton> {
),
color: foregroundColor,
icon: const Icon(Icons.send),
onPressed: _send));
onPressed: widget.enabled ? _send : null));
}
}

Expand All @@ -785,13 +812,15 @@ class _ComposeBoxLayout extends StatelessWidget {
required this.sendButton,
required this.contentController,
required this.contentFocusNode,
this.enabled = true,
});

final Widget? topicInput;
final Widget contentInput;
final Widget sendButton;
final ComposeContentController contentController;
final FocusNode contentFocusNode;
final bool enabled;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -835,9 +864,21 @@ class _ComposeBoxLayout extends StatelessWidget {
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),
_AttachFileButton(
contentController: contentController,
contentFocusNode: contentFocusNode,
enabled: enabled,
),
_AttachMediaButton(
contentController: contentController,
contentFocusNode: contentFocusNode,
enabled: enabled,
),
_AttachFromCameraButton(
contentController: contentController,
contentFocusNode: contentFocusNode,
enabled: enabled,
),
])),
])))); }
}
Expand Down Expand Up @@ -925,6 +966,20 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox

@override FocusNode get contentFocusNode => _contentFocusNode;
final _contentFocusNode = FocusNode();
late bool enabled;

@override
void didChangeDependencies() {
super.didChangeDependencies();
final store = PerAccountStoreWidget.of(context);
enabled = switch (widget.narrow) {
DmNarrow(:final otherRecipientIds) => otherRecipientIds.every((id) =>
store.users[id]?.isActive ?? true),
TopicNarrow() => true,
};

if (!enabled) _contentController.clear();
}

@override
void dispose() {
Expand All @@ -939,15 +994,18 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox
contentController: _contentController,
contentFocusNode: _contentFocusNode,
topicInput: null,
enabled: enabled,
contentInput: _FixedDestinationContentInput(
narrow: widget.narrow,
controller: _contentController,
focusNode: _contentFocusNode,
enabled: enabled,
),
sendButton: _SendButton(
topicController: null,
contentController: _contentController,
getDestination: () => widget.narrow.destination,
enabled: enabled,
));
}
}
Expand Down
Loading

0 comments on commit 3c48c68

Please sign in to comment.