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 17, 2024
1 parent 0c8a0a8 commit 4d106d6
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 15 deletions.
7 changes: 7 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@
"user": {"type": "String", "example": "channel name"}
}
},
"composeBoxDeactivatedDmContentHint": "You cannot send messages to {persons, plural, =1{a deactivated user} other{deactivated users}}.",
"@composeBoxDeactivatedDmContentHint": {
"description": "Hint text for content input when sending a message to one or multiple deactivated persons.",
"placeholders": {
"persons": {"type": "int", "example": "1"}
}
},
"composeBoxGroupDmContentHint": "Message group",
"@composeBoxGroupDmContentHint": {
"description": "Hint text for content input when sending a message to a group."
Expand Down
82 changes: 67 additions & 15 deletions lib/widgets/compose_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -271,16 +271,19 @@ 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;
if (!enabled) controller.clear();

return InputDecorator(
decoration: const InputDecoration(),
Expand All @@ -303,6 +306,7 @@ class _ContentInput extends StatelessWidget {
decoration: InputDecoration.collapsed(hintText: hintText),
maxLines: null,
textCapitalization: TextCapitalization.sentences,
enabled: enabled,
);
}),
));
Expand Down Expand Up @@ -377,31 +381,36 @@ 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(:final otherRecipientIds), false):
return zulipLocalizations.composeBoxDeactivatedDmContentHint(otherRecipientIds.length);

case (DmNarrow(), true): // A group DM thread.
return zulipLocalizations.composeBoxGroupDmContentHint;
}
}
Expand All @@ -412,7 +421,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 +502,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 +549,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 +593,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 +613,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 +634,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 +694,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 @@ -776,7 +805,7 @@ class _SendButtonState extends State<_SendButton> {
),
color: foregroundColor,
icon: const Icon(Icons.send),
onPressed: _send));
onPressed: widget.enabled ? _send : null));
}
}

Expand All @@ -787,13 +816,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 @@ -837,9 +868,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 @@ -937,19 +980,28 @@ class _FixedDestinationComposeBoxState extends State<_FixedDestinationComposeBox

@override
Widget build(BuildContext context) {
final store = PerAccountStoreWidget.of(context);
final bool enabled = switch (widget.narrow) {
DmNarrow(:final otherRecipientIds) => otherRecipientIds.every((id) =>
store.users[id]?.isActive ?? true),
TopicNarrow() => true,
};
return _ComposeBoxLayout(
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 4d106d6

Please sign in to comment.