Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/icons/ZulipIcons.ttf
Binary file not shown.
8 changes: 8 additions & 0 deletions assets/icons/video.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@
"@errorCouldNotEditMessageTitle": {
"description": "Error title when an exception prevented us from opening the compose box for editing a message."
},
"errorCouldNotAppendCallUrl": "Fail to get call URL",
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message has incorrect grammar. It should read "Failed to get call URL" instead of "Fail to get call URL".

Suggested change
"errorCouldNotAppendCallUrl": "Fail to get call URL",
"errorCouldNotAppendCallUrl": "Failed to get call URL",

Copilot uses AI. Check for mistakes.
"@errorCouldNotAppendCallUrl": {
"description": "Error title when fetching a call URL failed."
},
"successLinkCopied": "Link copied",
"@successLinkCopied": {
"description": "Success message after copy link action completed."
Expand Down Expand Up @@ -574,6 +578,14 @@
"@composeBoxAttachFromCameraTooltip": {
"description": "Tooltip for compose box icon to attach an image from the camera to the message."
},
"composeBoxAddVideoCallTooltip": "Add video call",
"@composeBoxAddVideoCallTooltip": {
"description": "Tooltip for compose box icon to add a video call url to the message."
},
"composeBoxAddVoiceCallTooltip": "Add voice call",
"@composeBoxAddVoiceCallTooltip": {
"description": "Tooltip for compose box icon to add a voice call url to the message."
},
"composeBoxGenericContentHint": "Type a message",
"@composeBoxGenericContentHint": {
"description": "Hint text for content input when sending a message."
Expand Down Expand Up @@ -654,6 +666,14 @@
"filename": {"type": "String", "example": "file.txt"}
}
},
"composeBoxVideoCallLinkText": "Join video call.",
"@composeBoxVideoCallLinkText": {
"description": "Text for a Markdown link to a video-call URL."
},
"composeBoxVoiceCallLinkText": "Join voice call.",
"@composeBoxVoiceCallLinkText": {
"description": "Text for a Markdown link to a voice-call URL."
},
"composeBoxLoadingMessage": "(loading message {messageId})",
"@composeBoxLoadingMessage": {
"description": "Placeholder in compose box showing the quoted message is currently loading.",
Expand Down
22 changes: 22 additions & 0 deletions lib/api/model/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ sealed class Event {
case 'update': return UserSettingsUpdateEvent.fromJson(json);
default: return UnexpectedEvent.fromJson(json);
}
case 'has_zoom_token': return HasZoomTokenEvent.fromJson(json);
case 'custom_profile_fields': return CustomProfileFieldsEvent.fromJson(json);
case 'user_group':
switch (json['op'] as String) {
Expand Down Expand Up @@ -1632,6 +1633,27 @@ enum ReactionOp {
remove,
}

/// A Zulip event of type `hasZoomToken`: https://zulip.com/api/get-events#has_zoom_token
@JsonSerializable(fieldRename: FieldRename.snake)
class HasZoomTokenEvent extends Event {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit-message nit:

api: Add event `hasZoomToken`

The event is named HasZoomTokenEvent in our code; hasZoomToken isn't a name we use for the event.

@override
@JsonKey(includeToJson: true)
String get type => 'has_zoom_token';

final bool value;

HasZoomTokenEvent({
required super.id,
required this.value,
});

factory HasZoomTokenEvent.fromJson(Map<String, dynamic> json) =>
_$HasZoomTokenEventFromJson(json);

@override
Map<String, dynamic> toJson() => _$HasZoomTokenEventToJson(this);
}

/// A Zulip event of type `heartbeat`: https://zulip.com/api/get-events#heartbeat
@JsonSerializable(fieldRename: FieldRename.snake)
class HeartbeatEvent extends Event {
Expand Down
13 changes: 13 additions & 0 deletions lib/api/model/events.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions lib/api/model/initial_snapshot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class InitialSnapshot {

final List<UserTopicItem> userTopics;

final bool hasZoomToken;

final GroupSettingValue? realmCanDeleteAnyMessageGroup; // TODO(server-10)

final GroupSettingValue? realmCanDeleteOwnMessageGroup; // TODO(server-10)
Expand All @@ -96,6 +98,10 @@ class InitialSnapshot {

final String realmName;

final RealmVideoChatProvider realmVideoChatProvider;

final String? realmJitsiServerUrl; // TODO(server-8)

/// The number of days until a user's account is treated as a full member.
///
/// Search for "realm_waiting_period_threshold" in https://zulip.com/api/register-queue.
Expand All @@ -113,14 +119,20 @@ class InitialSnapshot {

final Uri realmIconUrl;

final Map<String, RealmAvailableVideoChatProviders> realmAvailableVideoChatProviders;

final bool realmPresenceDisabled;

final Map<String, RealmDefaultExternalAccount> realmDefaultExternalAccounts;

final String? jitsiServerUrl; // TODO(server-8): Can ignore this deprecated field

final int maxFileUploadSizeMib;

final Uri serverEmojiDataUrl;

final String? serverJitsiServerUrl; // TODO(server-8)

final String? realmEmptyTopicDisplayName; // TODO(server-10)

@JsonKey(readValue: _readUsersIsActiveFallbackTrue)
Expand Down Expand Up @@ -182,22 +194,28 @@ class InitialSnapshot {
required this.userStatuses,
required this.userSettings,
required this.userTopics,
required this.hasZoomToken,
required this.realmCanDeleteAnyMessageGroup,
required this.realmCanDeleteOwnMessageGroup,
required this.realmDeleteOwnMessagePolicy,
required this.realmWildcardMentionPolicy,
required this.realmMandatoryTopics,
required this.realmName,
required this.realmVideoChatProvider,
required this.realmJitsiServerUrl,
required this.realmWaitingPeriodThreshold,
required this.realmMessageContentDeleteLimitSeconds,
required this.realmAllowMessageEditing,
required this.realmMessageContentEditLimitSeconds,
required this.realmEnableReadReceipts,
required this.realmIconUrl,
required this.realmAvailableVideoChatProviders,
required this.realmPresenceDisabled,
required this.realmDefaultExternalAccounts,
required this.jitsiServerUrl,
required this.maxFileUploadSizeMib,
required this.serverEmojiDataUrl,
required this.serverJitsiServerUrl,
required this.realmEmptyTopicDisplayName,
required this.realmUsers,
required this.realmNonActiveUsers,
Expand Down
34 changes: 34 additions & 0 deletions lib/api/model/initial_snapshot.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions lib/api/model/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,44 @@ class GroupSettingValueNameless extends GroupSettingValue {
Map<String, dynamic> toJson() => _$GroupSettingValueNamelessToJson(this);
}

/// As in [InitialSnapshot.realmAvailableVideoChatProviders].
///
/// For docs, search for "realm_available_video_chat_providers:"
/// in <https://zulip.com/api/register-queue>.
@JsonSerializable(fieldRename: FieldRename.snake)
class RealmAvailableVideoChatProviders {
final String name;
final int id;

RealmAvailableVideoChatProviders({
required this.name,
required this.id,
});

factory RealmAvailableVideoChatProviders.fromJson(Map<String, dynamic> json) =>
_$RealmAvailableVideoChatProvidersFromJson(json);

Map<String, dynamic> toJson() => _$RealmAvailableVideoChatProvidersToJson(this);
}

@JsonSerializable(fieldRename: FieldRename.snake)
class VideoCallResponse {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to define this here, instead of in lib/api/route/video_call.dart, which I think would be our normal pattern?

final String msg;
final String result;
final String url;

VideoCallResponse({
required this.msg,
required this.result,
required this.url,
});

factory VideoCallResponse.fromJson(Map<String, dynamic> json) =>
_$VideoCallResponseFromJson(json);
Comment on lines +91 to +92
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
factory VideoCallResponse.fromJson(Map<String, dynamic> json) =>
_$VideoCallResponseFromJson(json);
factory VideoCallResponse.fromJson(Map<String, dynamic> json) =>
_$VideoCallResponseFromJson(json);


Map<String, dynamic> toJson() => _$VideoCallResponseToJson(this);
}

/// As in [InitialSnapshot.customProfileFields].
///
/// For docs, search for "custom_profile_fields:"
Expand Down Expand Up @@ -406,6 +444,28 @@ enum Emojiset {
String toJson() => _$EmojisetEnumMap[this]!;
}

/// As in [InitialSnapshot.realmVideoChatProvider].
///
/// For docs, search for "realm_video_chat_provider:"
/// in <https://zulip.com/api/register-queue>.
@JsonEnum(fieldRename: FieldRename.snake, valueField: "apiValue")
enum RealmVideoChatProvider {
none(apiValue: 0),
jitsiMeet(apiValue: 1),
zoomUserOAuth(apiValue: 3),
bigBlueButton(apiValue: 4),
zoomServerToServerOAuth(apiValue: 5),
unknown(apiValue: null);

const RealmVideoChatProvider({
required this.apiValue,
});

final int? apiValue;

int? toJson() => apiValue;
}

/// As in [InitialSnapshot.realmUserGroups] or [UserGroupAddEvent].
@JsonSerializable(fieldRename: FieldRename.snake)
class UserGroup {
Expand Down
25 changes: 25 additions & 0 deletions lib/api/model/model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading