Skip to content
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

[Feature] #687463 - Call Bubble #270

Merged
merged 20 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions lib/blip_ds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export 'src/services/ds_auth.service.dart' show DSAuthService;
export 'src/services/ds_bottom_sheet.service.dart' show DSBottomSheetService;
export 'src/services/ds_dialog.service.dart' show DSDialogService;
export 'src/services/ds_file.service.dart' show DSFileService;
export 'src/services/ds_localization.service.dart' show DSLocalizationService;
export 'src/services/ds_media_format.service.dart' show DSMediaFormatService;
export 'src/services/ds_toast.service.dart' show DSToastService;
export 'src/themes/colors/ds_colors.theme.dart' show DSColors;
Expand Down Expand Up @@ -133,6 +134,8 @@ export 'src/widgets/chat/ds_contact_message_bubble.widget.dart'
show DSContactMessageBubble;
export 'src/widgets/chat/ds_delivery_report_icon.widget.dart'
show DSDeliveryReportIcon;
export 'src/widgets/chat/ds_end_calls_message_bubble.widget.dart'
show DSEndCallsMessageBubble;
export 'src/widgets/chat/ds_file_message_bubble.widget.dart'
show DSFileMessageBubble;
export 'src/widgets/chat/ds_image_message_bubble.widget.dart'
Expand Down
10 changes: 10 additions & 0 deletions lib/src/extensions/ds_localization.extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '../services/ds_localization.service.dart';

extension DSLocalizationExtension on String {
translate() {
final locale = DSLocalizationService.locale ?? 'pt_BR';
final translations =
DSLocalizationService.translations?[locale.toString()] ?? {};
return translations[this] ?? this;
}
}
13 changes: 13 additions & 0 deletions lib/src/services/ds_localization.service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';

abstract class DSLocalizationService {
static Locale? _locale;
static final Map<String, dynamic> _translations = {};

static setLocale(final Locale locale) => _locale = locale;
static setTranslations(final Map<String, dynamic> translations) =>
_translations.addAll(translations);

static Locale? get locale => _locale;
static Map<String, dynamic>? get translations => _translations;
}
11 changes: 6 additions & 5 deletions lib/src/themes/colors/ds_dark_colors.theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import 'package:flutter/material.dart';

/// All Dark [Color] constants that are used by this Design System.
abstract class DSDarkColors {
static const Color surfacePositive = Color(0xFF01562F);
static const Color surface0 = Color(0xFF424242);
static const Color surface1 = Color(0xFF292929);
static const Color surface1 = Color(0xFF393939);
static const Color surface3 = Color(0xFF141414);
static const Color contentDefault = Color(0xFFF6F6F6);
static const Color contentDefault = Color(0xFFFFFFFF);
static const Color success = Color(0xFF355E4B);
static const Color positive = Color(0xFF6BFFBC);
static const Color error = Color(0xFF7B3D3D);

static const Color extendBlue = Color(0xFF1968F0);
static const Color extendGreen = Color(0xFF35DE90);
static const Color success = Color(0xFF355E4B);
static const Color positive = Color(0xFF01562F);
static const Color error = Color(0xFF7B3D3D);
}
1 change: 1 addition & 0 deletions lib/src/utils/ds_message_content_type.util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ abstract class DSMessageContentType {
static const String location = 'application/vnd.lime.location+json';
static const String applicationJson = 'application/json';
static const String reply = 'application/vnd.lime.reply+json';
static const String callsMedia = 'application/vnd.iris.calls.media+json';
}
272 changes: 272 additions & 0 deletions lib/src/widgets/chat/ds_end_calls_message_bubble.widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import 'dart:async';

import 'package:flutter/material.dart';

import '../../enums/ds_align.enum.dart';
import '../../enums/ds_border_radius.enum.dart';
import '../../extensions/ds_localization.extension.dart';
import '../../extensions/ds_string.extension.dart';
import '../../models/ds_message_bubble_style.model.dart';
import '../../themes/colors/ds_colors.theme.dart';
import '../../themes/icons/ds_icons.dart';
import '../animations/ds_spinner_loading.widget.dart';
import '../buttons/ds_button.widget.dart';
import '../texts/ds_caption_small_text.widget.dart';
import '../texts/ds_caption_text.widget.dart';
import 'audio/ds_audio_player.widget.dart';
import 'ds_message_bubble.widget.dart';

class DSEndCallsMessageBubble extends StatelessWidget {
final DSAlign align;
final List<DSBorderRadius> borderRadius;
final DSMessageBubbleStyle style;
final Map<String, dynamic> content;
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
final Future<String?> Function(String)? onAsyncFetchSession;
final StreamController _streamController = StreamController<bool>();
final Map<String, dynamic>? translations;

bool get _isCallAnswered => ['completed', 'answer'].contains(
content['status'].toString().toLowerCase(),
);

bool get _isInbound =>
content['direction'].toString().toLowerCase() == 'inbound';

bool get _isLightBubbleBackground => style.isLightBubbleBackground(align);
bool get _isDefaultBubbleColors => style.isDefaultBubbleBackground(align);

Color get _foregroundColor => _isLightBubbleBackground
? DSColors.neutralDarkCity
: DSColors.neutralLightSnow;

DSEndCallsMessageBubble({
super.key,
required this.align,
required this.content,
required this.onAsyncFetchSession,
this.borderRadius = const [DSBorderRadius.all],
DSMessageBubbleStyle? style,
this.translations,
}) : style = style ?? DSMessageBubbleStyle();

@override
Widget build(BuildContext context) {
return DSMessageBubble(
borderRadius: borderRadius,
align: align,
style: style,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Column(
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
children: [
Container(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
decoration: BoxDecoration(
color: _isCallAnswered
? DSColors.success
: DSColors.error,
borderRadius: const BorderRadius.all(
Radius.circular(
8.0,
),
),
),
width: 30,
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
height: 30,
child: Icon(
_isCallAnswered
? _isInbound
? DSIcons.voip_receiving_outline
: DSIcons.voip_calling_outline
: DSIcons.voip_ended_outline,
size: 20.0,
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
),
),
],
),
),
Column(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DSCaptionText(
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
'calls.voice-text'.translate(),
fontWeight: FontWeight.bold,
color: _foregroundColor,
),
DSCaptionSmallText(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
_isCallAnswered
? 'calls.answered'.translate()
: 'calls.unanswered'.translate(),
color: _foregroundColor,
),
],
),
],
),
],
),
Flexible(
child: Column(
children: [
FutureBuilder(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
future:
content['identification'].toString().asPhoneNumber(),
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
return DSCaptionSmallText(
snapshot.data,
color: _foregroundColor,
);
}
return const DSSpinnerLoading();
},
)
],
),
),
],
),
if (_isCallAnswered && onAsyncFetchSession != null)
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
StreamBuilder(
stream: _streamController.stream,
builder: (_, __) {
return FutureBuilder<String?>(
future: onAsyncFetchSession!(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
content['sessionId'],
),
builder: (_, snapshot) {
return switch (snapshot.connectionState) {
ConnectionState.waiting => _buildLoading(),
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
ConnectionState.done => snapshot.hasError
? _buildError()
: snapshot.data?.isEmpty ?? true
? const SizedBox.shrink()
Copy link
Collaborator

Choose a reason for hiding this comment

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

If anything goes wrong, the user shouldn't be able to load the recording again?

Copy link
Collaborator

Choose a reason for hiding this comment

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

only if stream contains any error

: _buildAudioPlayer(snapshot.data!),
_ => const SizedBox.shrink(),
};
},
);
},
)
],
),
);
}

Widget _buildContainer(final Widget child) => SizedBox(
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
githubdoandre marked this conversation as resolved.
Show resolved Hide resolved
height: 60.0,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
decoration: BoxDecoration(
color: _isLightBubbleBackground
? DSColors.neutralLightWhisper
: DSColors.neutralDarkDesk,
borderRadius: const BorderRadius.all(
Radius.circular(
8.0,
),
),
),
child: child,
),
),
);

Widget _buildAudioPlayer(final String data) => _buildContainer(
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: DSAudioPlayer(
uri: Uri.parse(data),
controlForegroundColor: _isLightBubbleBackground
? DSColors.neutralDarkRooftop
: DSColors.neutralLightSnow,
labelColor: _isLightBubbleBackground
? DSColors.neutralDarkCity
: DSColors.neutralLightSnow,
bufferActiveTrackColor: _isLightBubbleBackground
? DSColors.neutralMediumWave
: DSColors.neutralMediumElephant,
bufferInactiveTrackColor: _isLightBubbleBackground
? DSColors.neutralDarkRooftop
: DSColors.neutralLightBox,
sliderActiveTrackColor: _isLightBubbleBackground
? DSColors.primaryNight
: DSColors.primaryLight,
sliderThumbColor: _isLightBubbleBackground
? DSColors.neutralDarkRooftop
: DSColors.neutralLightSnow,
speedForegroundColor: _isLightBubbleBackground
? DSColors.neutralDarkCity
: DSColors.neutralLightSnow,
speedBorderColor: _isLightBubbleBackground
? _isDefaultBubbleColors
? DSColors.neutralMediumSilver
: DSColors.neutralDarkCity
: _isDefaultBubbleColors
? DSColors.disabledText
: DSColors.neutralLightSnow,
),
),
);

Widget _buildLoading() => _buildContainer(
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const DSSpinnerLoading(),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: DSCaptionText(
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
'calls.preparing-record'.translate(),
color: _foregroundColor,
),
),
],
),
),
);

Widget _buildError() => _buildContainer(
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DSCaptionText(
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
'calls.load-record'.translate(),
color: _foregroundColor,
),
DSButton(
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
onPressed: () => _streamController.add(true),
borderColor: _isLightBubbleBackground
? DSColors.neutralMediumSilver
: DSColors.disabledText,
foregroundColor: _isLightBubbleBackground
? DSColors.neutralDarkCity
: DSColors.neutralLightSnow,
backgroundColor: _isLightBubbleBackground
? DSColors.neutralLightWhisper
: DSColors.neutralDarkDesk,
trailingIcon: const Icon(
DSIcons.refresh_outline,
size: 25.0,
adrianoct42 marked this conversation as resolved.
Show resolved Hide resolved
),
autoSize: true,
)
],
),
),
);
}
12 changes: 12 additions & 0 deletions lib/src/widgets/utils/ds_card.widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import '../chat/audio/ds_audio_message_bubble.widget.dart';
import '../chat/ds_application_json_message_bubble.widget.dart';
import '../chat/ds_carrousel.widget.dart';
import '../chat/ds_contact_message_bubble.widget.dart';
import '../chat/ds_end_calls_message_bubble.widget.dart';
import '../chat/ds_file_message_bubble.widget.dart';
import '../chat/ds_image_message_bubble.widget.dart';
import '../chat/ds_location_message_bubble.widget.dart';
Expand Down Expand Up @@ -49,6 +50,7 @@ class DSCard extends StatelessWidget {
this.showRequestLocationButton = false,
this.replyContent,
this.isUploading = false,
this.onAsyncFetchSession,
}) : style = style ?? DSMessageBubbleStyle();

final String type;
Expand All @@ -66,6 +68,7 @@ class DSCard extends StatelessWidget {
final bool showRequestLocationButton;
final DSReplyContent? replyContent;
final bool isUploading;
final Future<String?> Function(String)? onAsyncFetchSession;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -167,6 +170,15 @@ class DSCard extends StatelessWidget {
avatarConfig: avatarConfig,
);

case DSMessageContentType.callsMedia:
return DSEndCallsMessageBubble(
align: align,
borderRadius: borderRadius,
style: style,
content: content,
onAsyncFetchSession: onAsyncFetchSession,
);

default:
return DSUnsupportedContentMessageBubble(
align: align,
Expand Down
Loading
Loading