Skip to content

Commit b2bb501

Browse files
MritunjayTiwari14MritunjayTiwari14
authored andcommitted
compose_box: Add video chat service button
1 parent 90eabdc commit b2bb501

File tree

6 files changed

+131
-20
lines changed

6 files changed

+131
-20
lines changed

lib/api/model/initial_snapshot.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ class InitialSnapshot {
8888
/// Search for "realm_wildcard_mention_policy" in https://zulip.com/api/register-queue.
8989
final RealmWildcardMentionPolicy realmWildcardMentionPolicy;
9090

91-
final String? realmJitsiServerUrl;
92-
9391
final int realmVideoChatProvider;
9492

9593
final bool realmMandatoryTopics;
@@ -117,6 +115,8 @@ class InitialSnapshot {
117115

118116
final Map<String, RealmDefaultExternalAccount> realmDefaultExternalAccounts;
119117

118+
final String? jitsiServerUrl;
119+
120120
final int maxFileUploadSizeMib;
121121

122122
final Uri serverEmojiDataUrl;
@@ -185,7 +185,6 @@ class InitialSnapshot {
185185
required this.realmDeleteOwnMessagePolicy,
186186
required this.realmWildcardMentionPolicy,
187187
required this.realmMandatoryTopics,
188-
required this.realmJitsiServerUrl,
189188
required this.realmVideoChatProvider,
190189
required this.realmName,
191190
required this.realmWaitingPeriodThreshold,
@@ -196,6 +195,7 @@ class InitialSnapshot {
196195
required this.realmIconUrl,
197196
required this.realmPresenceDisabled,
198197
required this.realmDefaultExternalAccounts,
198+
required this.jitsiServerUrl,
199199
required this.maxFileUploadSizeMib,
200200
required this.serverEmojiDataUrl,
201201
required this.realmEmptyTopicDisplayName,

lib/api/model/initial_snapshot.g.dart

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/model/realm.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ mixin RealmStore on PerAccountStoreBase, UserGroupStore {
3232
Duration get serverTypingStartedWaitPeriod => Duration(milliseconds: serverTypingStartedWaitPeriodMilliseconds);
3333
int get serverTypingStartedWaitPeriodMilliseconds;
3434

35-
String? get realmJitsiServerUrl;
35+
String? get jitsiServerUrl;
3636
int get realmVideoChatProvider;
3737
//|//////////////////////////////////////////////////////////////
3838
// Realm settings.
@@ -166,10 +166,6 @@ mixin ProxyRealmStore on RealmStore {
166166
@override
167167
int get serverTypingStartedWaitPeriodMilliseconds => realmStore.serverTypingStartedWaitPeriodMilliseconds;
168168
@override
169-
String? get realmJitsiServerUrl => realmStore.realmJitsiServerUrl;
170-
@override
171-
int get realmVideoChatProvider => realmStore.realmVideoChatProvider;
172-
@override
173169
bool get realmAllowMessageEditing => realmStore.realmAllowMessageEditing;
174170
@override
175171
GroupSettingValue? get realmCanDeleteAnyMessageGroup => realmStore.realmCanDeleteAnyMessageGroup;
@@ -180,6 +176,10 @@ mixin ProxyRealmStore on RealmStore {
180176
@override
181177
bool get realmMandatoryTopics => realmStore.realmMandatoryTopics;
182178
@override
179+
int get realmVideoChatProvider => realmStore.realmVideoChatProvider;
180+
@override
181+
String? get jitsiServerUrl => realmStore.jitsiServerUrl;
182+
@override
183183
int get maxFileUploadSizeMib => realmStore.maxFileUploadSizeMib;
184184
@override
185185
int? get realmMessageContentDeleteLimitSeconds => realmStore.realmMessageContentDeleteLimitSeconds;
@@ -232,12 +232,12 @@ class RealmStoreImpl extends HasUserGroupStore with RealmStore {
232232
serverTypingStartedExpiryPeriodMilliseconds = initialSnapshot.serverTypingStartedExpiryPeriodMilliseconds,
233233
serverTypingStoppedWaitPeriodMilliseconds = initialSnapshot.serverTypingStoppedWaitPeriodMilliseconds,
234234
serverTypingStartedWaitPeriodMilliseconds = initialSnapshot.serverTypingStartedWaitPeriodMilliseconds,
235-
realmJitsiServerUrl = initialSnapshot.realmJitsiServerUrl,
236-
realmVideoChatProvider = initialSnapshot.realmVideoChatProvider,
237235
realmAllowMessageEditing = initialSnapshot.realmAllowMessageEditing,
238236
realmCanDeleteAnyMessageGroup = initialSnapshot.realmCanDeleteAnyMessageGroup,
239237
realmCanDeleteOwnMessageGroup = initialSnapshot.realmCanDeleteOwnMessageGroup,
240238
realmMandatoryTopics = initialSnapshot.realmMandatoryTopics,
239+
realmVideoChatProvider = initialSnapshot.realmVideoChatProvider,
240+
jitsiServerUrl = initialSnapshot.jitsiServerUrl,
241241
maxFileUploadSizeMib = initialSnapshot.maxFileUploadSizeMib,
242242
realmMessageContentDeleteLimitSeconds = initialSnapshot.realmMessageContentDeleteLimitSeconds,
243243
realmMessageContentEditLimitSeconds = initialSnapshot.realmMessageContentEditLimitSeconds,
@@ -377,10 +377,6 @@ class RealmStoreImpl extends HasUserGroupStore with RealmStore {
377377
@override
378378
final int serverTypingStartedWaitPeriodMilliseconds;
379379

380-
@override
381-
final String? realmJitsiServerUrl;
382-
@override
383-
final int realmVideoChatProvider;
384380
@override
385381
final bool realmAllowMessageEditing;
386382
@override
@@ -392,6 +388,10 @@ class RealmStoreImpl extends HasUserGroupStore with RealmStore {
392388
@override
393389
final bool realmMandatoryTopics;
394390
@override
391+
final int realmVideoChatProvider;
392+
@override
393+
final String? jitsiServerUrl;
394+
@override
395395
final int maxFileUploadSizeMib;
396396
@override
397397
final int? realmMessageContentDeleteLimitSeconds;

lib/widgets/compose_box.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,61 @@ Future<void> _uploadFiles({
10311031
}
10321032
}
10331033

1034+
class _AttachVideoChatUrlButton extends StatelessWidget {
1035+
const _AttachVideoChatUrlButton({
1036+
required this.controller,
1037+
required this.enabled,
1038+
});
1039+
1040+
final ComposeBoxController controller;
1041+
final bool enabled;
1042+
1043+
static const int jitsi = 1;
1044+
static const int zoom = 2; //TODO: Add other supported video chat providers
1045+
1046+
String _generateJitsiUrl(String serverUrl, String linkText) {
1047+
final id = List.generate(15, (_) => Random.secure().nextInt(10)).join();
1048+
return '[$linkText]($serverUrl/$id#config.startWithVideoMuted=false)\n';
1049+
}
1050+
1051+
String? _getMeetingUrl(BuildContext context, int? provider, String? jitsiServerUrl) {
1052+
final linkText = ZulipLocalizations.of(context).composeBoxAttachFromVideoCallTooltip;
1053+
1054+
switch (provider) {
1055+
case jitsi: return jitsiServerUrl == null ? null :_generateJitsiUrl(jitsiServerUrl, linkText);
1056+
case zoom: return '[$linkText](https://zoom.us/start/meeting)';
1057+
default: return null;
1058+
}
1059+
}
1060+
1061+
void _handlePress(BuildContext context) {
1062+
final store = PerAccountStoreWidget.of(context);
1063+
final url = _getMeetingUrl(context, store.realmVideoChatProvider, store.jitsiServerUrl);
1064+
if (url == null) return;
1065+
1066+
final contentController = controller.content;
1067+
final insertionRange = contentController.insertionIndex();
1068+
1069+
contentController.value = contentController.value.replaced(insertionRange, url);
1070+
contentController.selection = TextSelection.collapsed(
1071+
offset: insertionRange.start + url.length);
1072+
controller.contentFocusNode.requestFocus();
1073+
}
1074+
1075+
@override
1076+
Widget build(BuildContext context) {
1077+
final designVariables = DesignVariables.of(context);
1078+
final zulipLocalizations = ZulipLocalizations.of(context);
1079+
1080+
return SizedBox(
1081+
width: _composeButtonSize,
1082+
child: IconButton(
1083+
icon: Icon(ZulipIcons.video, color: designVariables.foreground.withFadedAlpha(0.5)),
1084+
tooltip: zulipLocalizations.composeBoxAttachFromVideoCallTooltip,
1085+
onPressed: enabled ? () => _handlePress(context) : null));
1086+
}
1087+
}
1088+
10341089
abstract class _AttachUploadsButton extends StatelessWidget {
10351090
const _AttachUploadsButton({required this.controller, required this.enabled});
10361091

@@ -1469,6 +1524,7 @@ abstract class _ComposeBoxBody extends StatelessWidget {
14691524
_AttachFileButton(controller: controller, enabled: composeButtonsEnabled),
14701525
_AttachMediaButton(controller: controller, enabled: composeButtonsEnabled),
14711526
_AttachFromCameraButton(controller: controller, enabled: composeButtonsEnabled),
1527+
_AttachVideoChatUrlButton(controller: controller, enabled: composeButtonsEnabled),
14721528
];
14731529

14741530
final topicInput = buildTopicInput();

test/example_data.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,9 +1335,8 @@ InitialSnapshot initialSnapshot({
13351335
RealmDeleteOwnMessagePolicy? realmDeleteOwnMessagePolicy,
13361336
RealmWildcardMentionPolicy? realmWildcardMentionPolicy,
13371337
bool? realmMandatoryTopics,
1338-
String? realmName,
1339-
String? realmJitsiServerUrl,
13401338
int? realmVideoChatProvider,
1339+
String? realmName,
13411340
int? realmWaitingPeriodThreshold,
13421341
int? realmMessageContentDeleteLimitSeconds,
13431342
bool? realmAllowMessageEditing,
@@ -1346,6 +1345,7 @@ InitialSnapshot initialSnapshot({
13461345
Uri? realmIconUrl,
13471346
bool? realmPresenceDisabled,
13481347
Map<String, RealmDefaultExternalAccount>? realmDefaultExternalAccounts,
1348+
String? jitsiServerUrl,
13491349
int? maxFileUploadSizeMib,
13501350
Uri? serverEmojiDataUrl,
13511351
String? realmEmptyTopicDisplayName,
@@ -1400,6 +1400,7 @@ InitialSnapshot initialSnapshot({
14001400
realmDeleteOwnMessagePolicy: realmDeleteOwnMessagePolicy,
14011401
realmWildcardMentionPolicy: realmWildcardMentionPolicy ?? RealmWildcardMentionPolicy.everyone,
14021402
realmMandatoryTopics: realmMandatoryTopics ?? true,
1403+
realmVideoChatProvider: realmVideoChatProvider ?? 1,
14031404
realmName: realmName ?? 'Example Zulip organization',
14041405
realmWaitingPeriodThreshold: realmWaitingPeriodThreshold ?? 0,
14051406
realmMessageContentDeleteLimitSeconds: realmMessageContentDeleteLimitSeconds,
@@ -1409,15 +1410,14 @@ InitialSnapshot initialSnapshot({
14091410
realmIconUrl: realmIconUrl ?? _realmIcon,
14101411
realmPresenceDisabled: realmPresenceDisabled ?? false,
14111412
realmDefaultExternalAccounts: realmDefaultExternalAccounts ?? {},
1413+
jitsiServerUrl: jitsiServerUrl ?? 'https://meet.jit.si',
14121414
maxFileUploadSizeMib: maxFileUploadSizeMib ?? 25,
14131415
serverEmojiDataUrl: serverEmojiDataUrl
14141416
?? realmUrl.replace(path: '/static/emoji.json'),
14151417
realmEmptyTopicDisplayName: realmEmptyTopicDisplayName ?? defaultRealmEmptyTopicDisplayName,
14161418
realmUsers: realmUsers ?? [selfUser],
14171419
realmNonActiveUsers: realmNonActiveUsers ?? [],
14181420
crossRealmBots: crossRealmBots ?? [],
1419-
realmJitsiServerUrl: realmJitsiServerUrl ?? 'https://meet.jit.si',
1420-
realmVideoChatProvider: realmVideoChatProvider ?? 0,
14211421
);
14221422
}
14231423
const _initialSnapshot = initialSnapshot;

test/widgets/compose_box_test.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ void main() {
6363
List<Subscription> subscriptions = const [],
6464
List<Message>? messages,
6565
bool? mandatoryTopics,
66+
int? realmVideoChatProvider,
67+
String? jitsiServerUrl,
6668
int? zulipFeatureLevel,
6769
}) async {
6870
streams ??= subscriptions;
@@ -89,6 +91,8 @@ void main() {
8991
subscriptions: subscriptions,
9092
zulipFeatureLevel: zulipFeatureLevel,
9193
realmMandatoryTopics: mandatoryTopics,
94+
realmVideoChatProvider: realmVideoChatProvider,
95+
jitsiServerUrl: jitsiServerUrl,
9296
realmAllowMessageEditing: true,
9397
realmMessageContentEditLimitSeconds: null,
9498
));
@@ -1037,6 +1041,56 @@ void main() {
10371041
});
10381042
});
10391043

1044+
group('video call button', () {
1045+
Future<void> prepare(WidgetTester tester, {
1046+
String? jitsiServerUrl,
1047+
int? realmVideoChatProvider,
1048+
}) async {
1049+
TypingNotifier.debugEnable = false;
1050+
addTearDown(TypingNotifier.debugReset);
1051+
1052+
final channel = eg.stream();
1053+
final narrow = ChannelNarrow(channel.streamId);
1054+
await prepareComposeBox(tester,
1055+
narrow: narrow,
1056+
streams: [channel],
1057+
jitsiServerUrl : jitsiServerUrl,
1058+
realmVideoChatProvider : realmVideoChatProvider,
1059+
);
1060+
1061+
await enterTopic(tester, narrow: narrow, topic: 'some topic');
1062+
await tester.pump();
1063+
}
1064+
1065+
group('attach video call link', () {
1066+
testWidgets('jitsi success', (tester) async {
1067+
await prepare(tester);
1068+
1069+
connection.prepare();
1070+
1071+
await tester.tap(find.byIcon(ZulipIcons.video));
1072+
await tester.pump();
1073+
1074+
check(controller!.content.text)
1075+
..startsWith('[Join video call.](https://meet.jit.si')
1076+
..endsWith('#config.startWithVideoMuted=false)\n');
1077+
});
1078+
1079+
testWidgets('zoom success', (tester) async {
1080+
await prepare(tester, jitsiServerUrl: '',
1081+
realmVideoChatProvider: 2);
1082+
1083+
connection.prepare();
1084+
1085+
await tester.tap(find.byIcon(ZulipIcons.video));
1086+
await tester.pump();
1087+
1088+
check(controller!.content.text)
1089+
.equals('[Join video call.](https://zoom.us/start/meeting)');
1090+
});
1091+
});
1092+
});
1093+
10401094
group('uploads', () {
10411095
void checkAppearsLoading(WidgetTester tester, bool expected) {
10421096
final sendButtonElement = tester.element(find.ancestor(
@@ -1314,6 +1368,7 @@ void main() {
13141368
check(attachButtonFinder(ZulipIcons.attach_file).evaluate().length).equals(areShown ? 1 : 0);
13151369
check(attachButtonFinder(ZulipIcons.image).evaluate().length).equals(areShown ? 1 : 0);
13161370
check(attachButtonFinder(ZulipIcons.camera).evaluate().length).equals(areShown ? 1 : 0);
1371+
check(attachButtonFinder(ZulipIcons.video).evaluate().length).equals(areShown ? 1 : 0);
13171372
}
13181373

13191374
void checkBannerWithLabel(String label, {required bool isShown}) {

0 commit comments

Comments
 (0)