diff --git a/assets/svg/ic_activate.svg b/assets/svg/ic_activate.svg new file mode 100644 index 00000000..fada1032 --- /dev/null +++ b/assets/svg/ic_activate.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_add.svg b/assets/svg/ic_add.svg new file mode 100644 index 00000000..73e8ce7f --- /dev/null +++ b/assets/svg/ic_add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/ic_arrow.svg b/assets/svg/ic_arrow.svg new file mode 100644 index 00000000..dde75b5f --- /dev/null +++ b/assets/svg/ic_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_big_trust.svg b/assets/svg/ic_big_trust.svg new file mode 100644 index 00000000..6578e037 --- /dev/null +++ b/assets/svg/ic_big_trust.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_big_trust_activated.svg b/assets/svg/ic_big_trust_activated.svg new file mode 100644 index 00000000..14c15f08 --- /dev/null +++ b/assets/svg/ic_big_trust_activated.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_block.svg b/assets/svg/ic_block.svg new file mode 100644 index 00000000..8385597a --- /dev/null +++ b/assets/svg/ic_block.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_contact_group.svg b/assets/svg/ic_contact_group.svg new file mode 100644 index 00000000..c24d2bf2 --- /dev/null +++ b/assets/svg/ic_contact_group.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/ic_image.svg b/assets/svg/ic_image.svg new file mode 100644 index 00000000..e43c4ce2 --- /dev/null +++ b/assets/svg/ic_image.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/svg/ic_trash.svg b/assets/svg/ic_trash.svg new file mode 100644 index 00000000..d4719184 --- /dev/null +++ b/assets/svg/ic_trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ic_trust.svg b/assets/svg/ic_trust.svg new file mode 100644 index 00000000..27103ea7 --- /dev/null +++ b/assets/svg/ic_trust.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/app.dart b/lib/app.dart index b553c55c..c070f41b 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,3 +1,4 @@ +import 'package:atsign_atmosphere_pro/view_models/add_contact_provider.dart'; import 'package:atsign_atmosphere_pro/view_models/file_download_checker.dart'; import 'package:atsign_atmosphere_pro/desktop_routes/desktop_routes.dart'; import 'package:atsign_atmosphere_pro/view_models/file_progress_provider.dart'; @@ -50,6 +51,7 @@ class _MyAppState extends State { ChangeNotifierProvider( create: (context) => SideBarProvider()), ChangeNotifierProvider(create: (context) => TrustedContactProvider()), + ChangeNotifierProvider(create: (context) => AddContactProvider()), ChangeNotifierProvider(create: (context) => NestedRouteProvider()), ChangeNotifierProvider(create: (context) => SwitchAtsignProvider()), ChangeNotifierProvider(create: (context) => FileDownloadChecker()), diff --git a/lib/screens/common_widgets/app_bar_custom.dart b/lib/screens/common_widgets/app_bar_custom.dart index 88aa02d4..2c0450dc 100644 --- a/lib/screens/common_widgets/app_bar_custom.dart +++ b/lib/screens/common_widgets/app_bar_custom.dart @@ -42,7 +42,7 @@ class AppBarCustom extends StatelessWidget implements PreferredSizeWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Expanded( + Flexible( child: Text( title ?? '', textAlign: TextAlign.left, diff --git a/lib/screens/common_widgets/avatar_widget.dart b/lib/screens/common_widgets/avatar_widget.dart new file mode 100644 index 00000000..71ad2f8d --- /dev/null +++ b/lib/screens/common_widgets/avatar_widget.dart @@ -0,0 +1,74 @@ +import 'dart:typed_data'; + +import 'package:at_contact/at_contact.dart'; +import 'package:at_contacts_flutter/widgets/custom_circle_avatar.dart'; +import 'package:at_contacts_group_flutter/widgets/contact_initial.dart'; +import 'package:flutter/material.dart'; + +class AvatarWidget extends StatefulWidget { + final AtContact contact; + final double? borderRadius; + final double size; + + const AvatarWidget({ + Key? key, + this.size = 40, + this.borderRadius, + required this.contact, + }) : super(key: key); + + @override + State createState() => _AvatarWidgetState(); +} + +class _AvatarWidgetState extends State { + String contactName = 'UG'; + Uint8List? image; + + @override + void initState() { + getNameAndImage(); + super.initState(); + } + + void getNameAndImage() { + try { + contactName = widget.contact.atSign ?? 'UG'; + + if (contactName[0] == '@') { + contactName = contactName.substring(1); + } + + if (widget.contact.tags != null && + widget.contact.tags?['image'] != null) { + List intList = widget.contact.tags!['image'].cast(); + image = Uint8List.fromList(intList); + } + } catch (e) { + contactName = 'UG'; + print('Error in getting image $e'); + } + } + + @override + Widget build(BuildContext context) { + return Container( + height: widget.size, + width: widget.size, + decoration: const BoxDecoration( + color: Colors.black, + shape: BoxShape.circle, + ), + child: image != null + ? CustomCircleAvatar( + byteImage: image, + nonAsset: true, + ) + : ContactInitial( + borderRadius: widget.borderRadius, + size: widget.size, + initials: contactName, + ), + ); + } +} diff --git a/lib/screens/common_widgets/card_widget.dart b/lib/screens/common_widgets/card_widget.dart new file mode 100644 index 00000000..72ef70e7 --- /dev/null +++ b/lib/screens/common_widgets/card_widget.dart @@ -0,0 +1,55 @@ +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class CardButton extends StatelessWidget { + final String icon; + final String title; + final TextStyle? style; + final Function()? onTap; + final Color? backgroundColor; + final Color? borderColor; + + const CardButton({ + Key? key, + required this.icon, + required this.title, + this.style, + this.onTap, + this.backgroundColor, + this.borderColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + height: 62, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: backgroundColor ?? ColorConstants.lightGrey, + border: Border.all( + color: borderColor ?? ColorConstants.grey, + ), + ), + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Row( + children: [ + SvgPicture.asset(icon), + const SizedBox(width: 8), + Text( + title, + style: style ?? + TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: ColorConstants.grey, + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/common_widgets/gradient_outline_input_border.dart b/lib/screens/common_widgets/gradient_outline_input_border.dart new file mode 100644 index 00000000..280532cd --- /dev/null +++ b/lib/screens/common_widgets/gradient_outline_input_border.dart @@ -0,0 +1,176 @@ +import 'dart:math' as math; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class GradientOutlineInputBorder extends InputBorder { + const GradientOutlineInputBorder({ + required this.gradient, + this.width = 1.0, + this.gapPadding = 4.0, + this.borderRadius = const BorderRadius.all(Radius.circular(4)), + }); + + final double width; + + final BorderRadius borderRadius; + + final Gradient gradient; + + final double gapPadding; + + @override + InputBorder copyWith({BorderSide? borderSide}) { + return this; + } + + @override + bool get isOutline => true; + + @override + EdgeInsetsGeometry get dimensions => EdgeInsets.all(width); + + @override + Path getInnerPath(Rect rect, {TextDirection? textDirection}) { + return Path() + ..addRRect( + borderRadius + .resolve(textDirection) + .toRRect(rect) + .deflate(borderSide.width), + ); + } + + @override + Path getOuterPath(Rect rect, {TextDirection? textDirection}) { + return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect)); + } + + @override + void paint( + Canvas canvas, + Rect rect, { + double? gapStart, + double gapExtent = 0.0, + double gapPercentage = 0.0, + TextDirection? textDirection, + }) { + final paint = _getPaint(rect); + final outer = borderRadius.toRRect(rect); + final center = outer.deflate(borderSide.width / 2.0); + if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) { + canvas.drawRRect(center, paint); + } else { + final extent = + lerpDouble(0.0, gapExtent + gapPadding * 2.0, gapPercentage)!; + switch (textDirection!) { + case TextDirection.rtl: + final path = _gapBorderPath( + canvas, + center, + math.max(0, gapStart + gapPadding - extent), + extent, + ); + canvas.drawPath(path, paint); + break; + + case TextDirection.ltr: + final path = _gapBorderPath( + canvas, + center, + math.max(0, gapStart - gapPadding), + extent, + ); + canvas.drawPath(path, paint); + break; + } + } + } + + @override + ShapeBorder scale(double t) { + return GradientOutlineInputBorder( + width: width * t, + borderRadius: borderRadius * t, + gradient: gradient, + ); + } + + Paint _getPaint(Rect rect) { + return Paint() + ..strokeWidth = width + ..shader = gradient.createShader(rect) + ..style = PaintingStyle.stroke; + } + + Path _gapBorderPath( + Canvas canvas, + RRect center, + double start, + double extent, + ) { + // When the corner radii on any side add up to be greater than the + // given height, each radius has to be scaled to not exceed the + // size of the width/height of the RRect. + final scaledRRect = center.scaleRadii(); + + final tlCorner = Rect.fromLTWH( + scaledRRect.left, + scaledRRect.top, + scaledRRect.tlRadiusX * 2.0, + scaledRRect.tlRadiusY * 2.0, + ); + final trCorner = Rect.fromLTWH( + scaledRRect.right - scaledRRect.trRadiusX * 2.0, + scaledRRect.top, + scaledRRect.trRadiusX * 2.0, + scaledRRect.trRadiusY * 2.0, + ); + final brCorner = Rect.fromLTWH( + scaledRRect.right - scaledRRect.brRadiusX * 2.0, + scaledRRect.bottom - scaledRRect.brRadiusY * 2.0, + scaledRRect.brRadiusX * 2.0, + scaledRRect.brRadiusY * 2.0, + ); + final blCorner = Rect.fromLTWH( + scaledRRect.left, + scaledRRect.bottom - scaledRRect.blRadiusY * 2.0, + scaledRRect.blRadiusX * 2.0, + scaledRRect.blRadiusX * 2.0, + ); + + const cornerArcSweep = math.pi / 2.0; + final tlCornerArcSweep = start < scaledRRect.tlRadiusX + ? math.asin((start / scaledRRect.tlRadiusX).clamp(-1.0, 1.0)) + : math.pi / 2.0; + + final path = Path() + ..addArc(tlCorner, math.pi, tlCornerArcSweep) + ..moveTo(scaledRRect.left + scaledRRect.tlRadiusX, scaledRRect.top); + + if (start > scaledRRect.tlRadiusX) { + path.lineTo(scaledRRect.left + start, scaledRRect.top); + } + + const trCornerArcStart = (3 * math.pi) / 2.0; + const trCornerArcSweep = cornerArcSweep; + if (start + extent < scaledRRect.width - scaledRRect.trRadiusX) { + path + ..relativeMoveTo(extent, 0) + ..lineTo(scaledRRect.right - scaledRRect.trRadiusX, scaledRRect.top) + ..addArc(trCorner, trCornerArcStart, trCornerArcSweep); + } else if (start + extent < scaledRRect.width) { + final dx = scaledRRect.width - (start + extent); + final sweep = math.acos(dx / scaledRRect.trRadiusX); + path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep); + } + + return path + ..moveTo(scaledRRect.right, scaledRRect.top + scaledRRect.trRadiusY) + ..lineTo(scaledRRect.right, scaledRRect.bottom - scaledRRect.brRadiusY) + ..addArc(brCorner, 0, cornerArcSweep) + ..lineTo(scaledRRect.left + scaledRRect.blRadiusX, scaledRRect.bottom) + ..addArc(blCorner, math.pi / 2.0, cornerArcSweep) + ..lineTo(scaledRRect.left, scaledRRect.top + scaledRRect.tlRadiusY); + } +} \ No newline at end of file diff --git a/lib/screens/common_widgets/gradient_text_field_widget.dart b/lib/screens/common_widgets/gradient_text_field_widget.dart new file mode 100644 index 00000000..1d0e0dc8 --- /dev/null +++ b/lib/screens/common_widgets/gradient_text_field_widget.dart @@ -0,0 +1,77 @@ +import 'package:atsign_atmosphere_pro/screens/common_widgets/gradient_outline_input_border.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class GradientTextFieldWidget extends StatefulWidget { + final String? hintText; + final TextStyle? hintTextStyle; + final TextEditingController? controller; + final Function(String value)? onchange; + final Function(String value)? onSubmitted; + final String? prefixText; + final TextStyle? prefixStyle; + + const GradientTextFieldWidget({ + Key? key, + this.hintText, + this.controller, + this.hintTextStyle, + this.onchange, + this.onSubmitted, + this.prefixText, + this.prefixStyle, + }) : super(key: key); + + @override + State createState() => + _GradientTextFieldWidgetState(); +} + +class _GradientTextFieldWidgetState extends State { + @override + Widget build(BuildContext context) { + return SizedBox( + height: 44, + child: TextFormField( + controller: widget.controller, + onChanged: (value) { + widget.onchange?.call(value); + }, + onFieldSubmitted: (value) { + widget.onSubmitted?.call(value); + }, + decoration: InputDecoration( + prefixText: widget.prefixText, + prefixStyle: widget.prefixStyle, + border: GradientOutlineInputBorder( + gradient: LinearGradient( + colors: [ + ColorConstants.orangeColor, + ColorConstants.yellow.withOpacity(0.65), + ], + ), + width: 2, + borderRadius: BorderRadius.circular(10), + ), + focusedBorder: GradientOutlineInputBorder( + gradient: LinearGradient( + colors: [ + ColorConstants.orangeColor, + ColorConstants.yellow.withOpacity(0.65), + ], + ), + width: 2, + borderRadius: BorderRadius.circular(10), + ), + hintText: widget.hintText, + hintStyle: widget.hintTextStyle ?? + TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: ColorConstants.grey, + ), + ), + ), + ); + } +} diff --git a/lib/screens/common_widgets/header_widget.dart b/lib/screens/common_widgets/header_widget.dart new file mode 100644 index 00000000..5b7ef28f --- /dev/null +++ b/lib/screens/common_widgets/header_widget.dart @@ -0,0 +1,150 @@ +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/utils/vectors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class HeaderWidget extends StatefulWidget { + final Function()? onReloadCallback; + final TextEditingController? controller; + final Function(String)? onSearch; + final EdgeInsetsGeometry? margin; + + const HeaderWidget({ + Key? key, + this.onReloadCallback, + this.controller, + this.onSearch, + this.margin, + }) : super(key: key); + + @override + State createState() => _HeaderWidgetState(); +} + +class _HeaderWidgetState extends State { + bool isSearch = false; + + @override + Widget build(BuildContext context) { + return Container( + margin: widget.margin ?? const EdgeInsets.symmetric(horizontal: 28), + padding: const EdgeInsets.fromLTRB(14, 11, 8, 14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: ColorConstants.textBoxBg, + ), + child: Row( + children: [ + _buildButton( + title: "Refresh", + icon: AppVectors.icReload, + onTap: widget.onReloadCallback, + ), + const SizedBox(width: 24), + Expanded( + child: _buildSearchWidget(), + ), + ], + ), + ); + } + + Widget _buildButton({ + String? title, + required String icon, + Function()? onTap, + }) { + return InkWell( + onTap: () { + onTap?.call(); + }, + child: Column( + children: [ + Text( + title ?? '', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: ColorConstants.sidebarTextUnselected, + ), + ), + const SizedBox(height: 5), + Container( + height: 48, + width: 48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: ColorConstants.grey, + ), + color: Colors.white, + ), + child: Center( + child: SvgPicture.asset( + icon, + color: ColorConstants.grey, + ), + ), + ) + ], + ), + ); + } + + Widget _buildSearchWidget() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Search", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: ColorConstants.sidebarTextUnselected, + ), + ), + const SizedBox(height: 5), + Container( + height: 48, + margin: const EdgeInsets.only(right: 12), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.only(left: 6, right: 8), + child: Row( + children: [ + Expanded( + child: TextField( + controller: widget.controller, + decoration: InputDecoration.collapsed( + hintText: 'Search History by atSign', + hintStyle: TextStyle( + color: ColorConstants.grey, + fontSize: 14, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic, + ), + ), + onChanged: widget.onSearch, + ), + ), + SizedBox(width: 4), + SizedBox( + width: 20, + height: 20, + child: SvgPicture.asset( + AppVectors.icSearch, + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/screens/contact_new_version/add_contact_screen.dart b/lib/screens/contact_new_version/add_contact_screen.dart new file mode 100644 index 00000000..c088df95 --- /dev/null +++ b/lib/screens/contact_new_version/add_contact_screen.dart @@ -0,0 +1,218 @@ +import 'package:atsign_atmosphere_pro/screens/common_widgets/gradient_text_field_widget.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/view_models/add_contact_provider.dart'; +import 'package:atsign_atmosphere_pro/view_models/base_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AddContactScreen extends StatefulWidget { + const AddContactScreen({Key? key}) : super(key: key); + + @override + State createState() => _AddContactScreenState(); +} + +class _AddContactScreenState extends State { + late TextEditingController atSignController; + late TextEditingController nicknameController; + late AddContactProvider addContactProvider, state; + + @override + void initState() { + addContactProvider = context.read(); + atSignController = TextEditingController(); + nicknameController = TextEditingController(); + super.initState(); + addContactProvider.initData(); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (_c, provider, _) { + state = context.watch(); + return Scaffold( + backgroundColor: Colors.transparent, + resizeToAvoidBottomInset: false, + body: Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only(top: 120), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + offset: const Offset(0, 4), + ) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 2, + width: 45, + margin: const EdgeInsets.only(left: 27, top: 38), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20), + ), + ), + const SizedBox(height: 24), + Expanded( + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 27), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Add Contact", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + const SizedBox(height: 53), + GradientTextFieldWidget( + hintText: 'Enter atSign', + controller: atSignController, + prefixText: "@", + prefixStyle: TextStyle( + fontSize: 14, + color: Colors.black, + ), + onSubmitted: (value) { + _checkValid(); + }, + ), + Visibility( + visible: state.atSignError.isNotEmpty, + child: Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + state.atSignError, + style: const TextStyle( + color: Colors.red, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + const SizedBox(height: 24), + GradientTextFieldWidget( + hintText: 'Enter nickname', + controller: nicknameController, + onSubmitted: (value) { + _checkValid(); + }, + ), + const SizedBox(height: 44), + Container( + height: 1, + decoration: BoxDecoration( + color: ColorConstants.darkGray, + ), + ), + const SizedBox(height: 24), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + "atSign verified", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Color(0xffCACACA), + ), + ), + const SizedBox(width: 10), + Icon( + Icons.check_circle_outlined, + color: state.isVerify + ? Colors.green + : ColorConstants.darkGray, + ) + ], + ), + ], + ), + ), + state.status['add_contact_status'] == Status.Loading + ? Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + ColorConstants.orange, + ), + ), + ) + : SizedBox(), + ], + ), + ), + SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.fromLTRB(27, 0, 27, 40), + child: InkWell( + onTap: () async { + if (addContactProvider.isVerify) { + var response = await addContactProvider.addContact( + atSign: atSignController.text, + nickname: nicknameController.text, + ); + + if (response ?? false) { + Navigator.of(context).pop(true); + } + } + }, + child: Container( + height: 60, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + "Create New Contact", + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + }, + ); + } + + void _checkValid() { + if (atSignController.text.isNotEmpty && + nicknameController.text.isNotEmpty) { + addContactProvider.changeVerifyStatus(true); + } else { + addContactProvider.changeVerifyStatus(false); + } + } +} diff --git a/lib/screens/contact_new_version/blocked_contact_screen.dart b/lib/screens/contact_new_version/blocked_contact_screen.dart new file mode 100644 index 00000000..4d0c4359 --- /dev/null +++ b/lib/screens/contact_new_version/blocked_contact_screen.dart @@ -0,0 +1,280 @@ +import 'package:at_contacts_flutter/models/contact_base_model.dart'; +import 'package:at_contacts_flutter/services/contact_service.dart'; +import 'package:atsign_atmosphere_pro/screens/common_widgets/header_widget.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class BlockedContactScreen extends StatefulWidget { + const BlockedContactScreen({Key? key}) : super(key: key); + + @override + State createState() => _BlockedContactScreenState(); +} + +class _BlockedContactScreenState extends State { + late ContactService _contactService; + late TextEditingController searchController; + + @override + void initState() { + super.initState(); + _contactService = ContactService(); + searchController = TextEditingController(); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + await _contactService.fetchBlockContactList(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Align( + alignment: Alignment.bottomCenter, + child: Container( + height: MediaQuery.of(context).size.height - 120, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + offset: const Offset(0, 4), + ) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + _buildHeaderWidget(), + const SizedBox(height: 24), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 27), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Blocked atSigns", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + const SizedBox(height: 30), + HeaderWidget( + margin: EdgeInsets.only(bottom: 28), + onReloadCallback: () async { + await _contactService.fetchBlockContactList(); + searchController.clear(); + }, + controller: searchController, + onSearch: (value) { + setState(() {}); + }, + ), + Container( + height: 37, + padding: const EdgeInsets.only(left: 24), + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10), + ), + color: ColorConstants.textBoxBg), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "atSign", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: ColorConstants.sidebarTextUnselected, + ), + ), + Icon( + Icons.arrow_downward_outlined, + color: ColorConstants.sidebarTextUnselected, + ) + ], + ), + ), + ], + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 27), + child: _buildListBlocked(), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildHeaderWidget() { + return Padding( + padding: const EdgeInsets.fromLTRB(27, 24, 27, 0), + child: Row( + children: [ + Container( + height: 2, + width: 45, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20), + ), + ), + const Spacer(), + Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + height: 31, + alignment: Alignment.topRight, + padding: const EdgeInsets.symmetric( + horizontal: 30, + ), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + borderRadius: BorderRadius.circular(28), + ), + child: Center( + child: Text( + "Close", + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + color: ColorConstants.grey, + ), + ), + ), + ), + ), + ), + ], + ), + ); + } + + _buildListBlocked() { + return StreamBuilder>( + stream: _contactService.blockedContactStream, + initialData: _contactService.baseBlockedList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.active) { + var listContact = snapshot.data!; + listContact = listContact + .where( + (element) => (element?.contact?.atSign ?? '') + .contains(searchController.text), + ) + .toList(); + return ListView.builder( + itemCount: listContact.length, + physics: ClampingScrollPhysics(), + padding: EdgeInsets.zero, + itemBuilder: (context, index) { + return Container( + height: 58, + color: Colors.white, + child: Column( + children: [ + Expanded( + child: Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 18), + child: Text( + listContact[index]?.contact?.atSign ?? '', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: ColorConstants.textBlack, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(right: 24), + child: InkWell( + onTap: () async { + await _contactService.blockUnblockContact( + contact: listContact[index]!.contact!, + blockAction: false, + ); + }, + child: Container( + height: 31, + padding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 7, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: ColorConstants.boxGrey, + border: Border.all( + color: ColorConstants.grey, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Unblock?", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: ColorConstants.grey, + ), + ), + const SizedBox(width: 16), + const Icon( + Icons.block, + color: Colors.red, + size: 16, + ) + ], + ), + ), + ), + ) + ], + ), + ), + Container( + color: ColorConstants.textBoxBg, + height: 1, + width: double.infinity, + ) + ], + ), + ); + }, + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ); + } +} diff --git a/lib/screens/contact_new_version/contact_detail_screen.dart b/lib/screens/contact_new_version/contact_detail_screen.dart new file mode 100644 index 00000000..53239ec0 --- /dev/null +++ b/lib/screens/contact_new_version/contact_detail_screen.dart @@ -0,0 +1,260 @@ +import 'package:at_contact/at_contact.dart'; +import 'package:at_contacts_flutter/services/contact_service.dart'; +import 'package:atsign_atmosphere_pro/screens/common_widgets/avatar_widget.dart'; +import 'package:atsign_atmosphere_pro/screens/common_widgets/card_widget.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/utils/vectors.dart'; +import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class ContactDetailScreen extends StatefulWidget { + final AtContact contact; + + const ContactDetailScreen({ + Key? key, + required this.contact, + }) : super(key: key); + + @override + State createState() => _ContactDetailScreenState(); +} + +class _ContactDetailScreenState extends State { + late TrustedContactProvider _trustedContactProvider; + late ContactService _contactService; + + bool isTrusted = false; + + @override + void initState() { + _trustedContactProvider = TrustedContactProvider(); + _contactService = ContactService(); + checkTrustedContact(); + super.initState(); + } + + void checkTrustedContact() { + _trustedContactProvider.trustedContacts.forEach((element) { + if (element.atSign == widget.contact.atSign) { + setState(() { + isTrusted = true; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Align( + alignment: Alignment.bottomCenter, + child: Container( + height: double.infinity, + width: double.infinity, + margin: EdgeInsets.only(top: 120), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + offset: const Offset(0, 4), + ) + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + const Spacer(), + Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + height: 31, + alignment: Alignment.topRight, + padding: const EdgeInsets.symmetric( + horizontal: 30, + ), + margin: const EdgeInsets.only(right: 27, top: 30), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + borderRadius: BorderRadius.circular(28), + ), + child: Center( + child: Text( + "Close", + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + color: ColorConstants.grey, + ), + ), + ), + ), + ), + ), + ], + ), + const SizedBox(height: 11), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 42), + child: Row( + children: [ + AvatarWidget( + size: 83, + borderRadius: 24, + contact: widget.contact, + ), + const SizedBox(width: 25), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + widget.contact.atSign ?? '', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 5), + Flexible( + child: Text( + widget.contact.tags?['name'] ?? + widget.contact.atSign!.substring(1), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ) + ], + ), + ), + const SizedBox(height: 25), + Flexible( + child: SingleChildScrollView( + physics: ClampingScrollPhysics(), + padding: const EdgeInsets.symmetric( + horizontal: 36, + vertical: 25, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 63, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xfff05e3f), + Color(0xffeaa743), + ], + ), + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + "Transfer Now", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + const SizedBox(width: 24), + SvgPicture.asset( + AppVectors.icArrow, + ), + ], + ), + ), + ), + const SizedBox(height: 46), + isTrusted + ? CardButton( + icon: AppVectors.icBigTrustActivated, + title: "Trusted", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: ColorConstants.orange, + ), + borderColor: ColorConstants.orange, + backgroundColor: + ColorConstants.orange.withOpacity(0.2), + onTap: () async { + await _trustedContactProvider + .removeTrustedContacts(widget.contact); + setState(() { + isTrusted = false; + }); + }, + ) + : CardButton( + icon: AppVectors.icTrust, + title: "Add To Trusted", + onTap: () async { + await _trustedContactProvider + .addTrustedContacts(widget.contact); + setState(() { + isTrusted = true; + }); + }, + ), + const SizedBox(height: 25), + CardButton( + icon: AppVectors.icTrash, + title: "Delete", + onTap: () async { + await _contactService.deleteAtSign( + atSign: widget.contact.atSign!, + ); + Navigator.of(context).pop(true); + }, + ), + const SizedBox(height: 25), + CardButton( + icon: AppVectors.icBlock, + title: "Block", + onTap: () async { + await _contactService.blockUnblockContact( + contact: widget.contact, + blockAction: true, + ); + Navigator.of(context).pop(true); + }, + ), + const SizedBox(height: 25), + ], + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/contact_new_version/contact_screen.dart b/lib/screens/contact_new_version/contact_screen.dart new file mode 100644 index 00000000..9e2bf444 --- /dev/null +++ b/lib/screens/contact_new_version/contact_screen.dart @@ -0,0 +1,199 @@ +import 'package:at_contacts_group_flutter/screens/new_version/contact_screen.dart'; +import 'package:at_contacts_group_flutter/services/group_service.dart'; +import 'package:atsign_atmosphere_pro/screens/common_widgets/app_bar_custom.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/add_contact_screen.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/blocked_contact_screen.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/contact_detail_screen.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/group_contact_screen.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/trusted_contact_screen.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/utils/vectors.dart'; +import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; + +class ContactScreen extends StatefulWidget { + const ContactScreen({Key? key}) : super(key: key); + + @override + State createState() => _ContactScreenState(); +} + +class _ContactScreenState extends State { + late TrustedContactProvider trustedProvider; + late GroupService _groupService; + + @override + void initState() { + trustedProvider = context.read(); + _groupService = GroupService(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBarCustom( + height: 130, + title: "Contacts", + description: '${_groupService.listContact.length}', + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 30), + child: InkWell( + onTap: () async { + final result = await showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return AddContactScreen(); + }, + ); + if (result == true) { + reloadPage(); + } + }, + child: SvgPicture.asset( + AppVectors.icAdd, + ), + ), + ), + ), + body: buildBody(), + ); + } + + Widget buildBody() { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Row( + children: [ + _buildHeaderItem( + title: 'Blocked atSign', + icon: AppVectors.icBlock, + onTap: () async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return BlockedContactScreen(); + }, + ); + reloadPage(); + }, + ), + _buildHeaderItem( + title: 'Trusted Senders', + icon: AppVectors.icTrust, + onTap: () async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return TrustedContactScreen(); + }, + ); + reloadPage(); + }, + ), + _buildHeaderItem( + title: 'My Groups', + icon: AppVectors.icContactGroup, + onTap: () { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return GroupContactScreen(); + }, + ); + }, + ), + ], + ), + ), + Expanded( + child: ListContactScreen( + contactsTrusted: trustedProvider.trustedContacts, + onTapContact: (contact) async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return ContactDetailScreen( + contact: contact, + ); + }, + ); + + reloadPage(); + }, + ), + ), + SizedBox(height: 80), + ], + ); + } + + Widget _buildHeaderItem({ + required String title, + required String icon, + required Function onTap, + }) { + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: InkWell( + onTap: () { + onTap.call(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 12), + decoration: BoxDecoration( + color: ColorConstants.fadedGreyN, + border: Border.all( + color: ColorConstants.grey, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset(icon), + SizedBox(height: 8), + Text( + title, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: ColorConstants.grey, + ), + textAlign: TextAlign.center, + ) + ], + ), + ), + ), + ), + ); + } + + void reloadPage() async { + await Future.delayed(Duration(milliseconds: 500), () async { + await _groupService.fetchGroupsAndContacts(); + setState(() {}); + }); + } +} diff --git a/lib/screens/contact_new_version/group_contact_screen.dart b/lib/screens/contact_new_version/group_contact_screen.dart new file mode 100644 index 00000000..7dd7aa3f --- /dev/null +++ b/lib/screens/contact_new_version/group_contact_screen.dart @@ -0,0 +1,196 @@ +import 'package:at_contacts_group_flutter/screens/group_view/group_view.dart'; +import 'package:at_contacts_group_flutter/screens/new_group/create_group.dart'; +import 'package:at_contacts_group_flutter/screens/new_version/contact_screen.dart'; +import 'package:at_contacts_group_flutter/services/group_service.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class GroupContactScreen extends StatefulWidget { + const GroupContactScreen({Key? key}) : super(key: key); + + @override + State createState() => _GroupContactScreenState(); +} + +class _GroupContactScreenState extends State { + late GroupService groupService; + late TrustedContactProvider trustedProvider; + + @override + void initState() { + groupService = GroupService(); + trustedProvider = context.read(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Align( + alignment: Alignment.bottomCenter, + child: Container( + height: MediaQuery.of(context).size.height - 120, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + offset: const Offset(0, 4), + ) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(27, 24, 27, 0), + child: Row( + children: [ + Container( + height: 2, + width: 45, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20), + ), + ), + const Spacer(), + Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + height: 31, + alignment: Alignment.topRight, + padding: const EdgeInsets.symmetric( + horizontal: 30, + ), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + borderRadius: BorderRadius.circular(28), + ), + child: Center( + child: Text( + "Close", + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + color: ColorConstants.grey, + ), + ), + ), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 24), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(left: 27), + child: Text( + "My Groups", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ), + const SizedBox(height: 30), + Expanded( + child: ListContactScreen( + showGroups: true, + showContacts: false, + isHiddenAlpha: true, + onTapGroup: (group) async { + WidgetsBinding.instance + .addPostFrameCallback((_) async { + groupService.groupViewSink.add(group); + }); + + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => GroupView( + group: group, + ), + ), + ); + }, + ), + ), + SafeArea( + child: Padding( + padding: const EdgeInsets.only(bottom: 24, top: 18), + child: InkWell( + onTap: () async { + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => CreateGroupScreen( + trustContacts: + trustedProvider.trustedContacts, + ), + ), + ); + + if (result == true) { + await groupService.fetchGroupsAndContacts(); + setState(() {}); + } + }, + child: Container( + height: 67, + margin: const EdgeInsets.symmetric(horizontal: 27), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + gradient: LinearGradient( + colors: [ + const Color(0xfff05e3f), + const Color(0xffeaa743).withOpacity(0.65), + ], + ), + ), + child: const Center( + child: Text( + "Create Group", + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/contact_new_version/trusted_contact_screen.dart b/lib/screens/contact_new_version/trusted_contact_screen.dart new file mode 100644 index 00000000..ac0f22dd --- /dev/null +++ b/lib/screens/contact_new_version/trusted_contact_screen.dart @@ -0,0 +1,208 @@ +import 'package:at_contact/at_contact.dart'; +import 'package:at_contacts_group_flutter/screens/new_version/widget/single_contact_widget.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/contact_detail_screen.dart'; +import 'package:atsign_atmosphere_pro/utils/colors.dart'; +import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class TrustedContactScreen extends StatefulWidget { + const TrustedContactScreen({Key? key}) : super(key: key); + + @override + State createState() => _TrustedContactScreenState(); +} + +class _TrustedContactScreenState extends State { + late TrustedContactProvider provider; + late TextEditingController searchController; + + List trustedContacts = []; + + @override + void initState() { + provider = context.read(); + searchController = TextEditingController(); + super.initState(); + trustedContacts = provider.trustedContacts; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only(top: 120), + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + offset: const Offset(0, 4), + ) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + _buildHeaderWidget(), + const SizedBox(height: 24), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 27), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Trusted Senders", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + const SizedBox(height: 30), + Container( + height: 48, + margin: const EdgeInsets.only(right: 12), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + Expanded( + child: TextField( + controller: searchController, + onChanged: (value) { + setState(() { + trustedContacts = provider.trustedContacts + .where((element) => (element.atSign ?? '') + .contains(value)) + .toList(); + }); + }, + decoration: InputDecoration.collapsed( + hintText: 'Search by atSign or nickname', + hintStyle: TextStyle( + color: ColorConstants.grey, + fontSize: 14, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic, + ), + ), + // onChanged: widget.onSearch, + ), + ), + SizedBox( + width: 20, + height: 20, + child: Icon( + Icons.search, + color: ColorConstants.grey, + ), + ), + ], + ), + ), + const SizedBox(height: 24), + ], + ), + ), + Expanded( + child: Consumer( + builder: (context, myProvider, child) { + return Scrollbar( + child: SingleContactWidget( + contacts: trustedContacts, + onTapContact: (contact) async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 120), + child: ContactDetailScreen( + contact: contact, + ), + ); + }, + ); + }, + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildHeaderWidget() { + return Padding( + padding: const EdgeInsets.fromLTRB(27, 24, 27, 0), + child: Row( + children: [ + Container( + height: 2, + width: 45, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20), + ), + ), + const Spacer(), + Align( + alignment: Alignment.topRight, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + height: 31, + alignment: Alignment.topRight, + padding: const EdgeInsets.symmetric( + horizontal: 30, + ), + decoration: BoxDecoration( + border: Border.all( + color: ColorConstants.grey, + ), + borderRadius: BorderRadius.circular(28), + ), + child: Center( + child: Text( + "Close", + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + color: ColorConstants.grey, + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/welcome_screen/welcome_screen.dart b/lib/screens/welcome_screen/welcome_screen.dart index 7b310534..c90afe83 100644 --- a/lib/screens/welcome_screen/welcome_screen.dart +++ b/lib/screens/welcome_screen/welcome_screen.dart @@ -1,7 +1,7 @@ -import 'package:at_contacts_flutter/screens/contacts_screen.dart'; import 'package:at_contacts_flutter/utils/init_contacts_service.dart'; import 'package:at_contacts_group_flutter/services/group_service.dart'; import 'package:atsign_atmosphere_pro/screens/common_widgets/error_screen.dart'; +import 'package:atsign_atmosphere_pro/screens/contact_new_version/contact_screen.dart'; import 'package:atsign_atmosphere_pro/screens/history/history_screen.dart'; import 'package:atsign_atmosphere_pro/screens/history/transfer_history_screen.dart'; import 'package:atsign_atmosphere_pro/screens/my_files/my_files_screen.dart'; @@ -101,7 +101,7 @@ class _WelcomeScreenState extends State { static List _bottomSheetWidgetOptions = [ WelcomeScreenHome(), - ContactsScreen(), + ContactScreen(), MyFilesScreen(), TransferHistoryScreen(), SettingsScreen() diff --git a/lib/utils/colors.dart b/lib/utils/colors.dart index 8e73c4f1..2301abe9 100644 --- a/lib/utils/colors.dart +++ b/lib/utils/colors.dart @@ -42,6 +42,11 @@ class ColorConstants { static const Color fadedGreyN = Color(0xFFF1F1F1); static const Color dividerGrey = Color(0xFFD9D9D9); static const Color grey = Color(0xFF939393); + static const Color orange = Color(0xFFF07C50); + static const Color lightGrey = Color(0xFFF1F1F1); + static const Color boxGrey = Color(0xFFEFEFEF); + static const Color darkGray = Color(0xFFC0C0C0); + static const Color buttonGrey = Color(0xFFC7C7C7); static const Color sidebarTextUnselected = Color(0xFFA4A4A5); static const Color sidebarTextSelected = Color(0xFF000000); diff --git a/lib/utils/vectors.dart b/lib/utils/vectors.dart index 125351d7..dcde9d8e 100644 --- a/lib/utils/vectors.dart +++ b/lib/utils/vectors.dart @@ -13,4 +13,14 @@ class AppVectors { static String icReceiveBorder = '$_basePath/ic_receive_border.svg'; static String icSendBorder = '$_basePath/ic_send_border.svg'; static String icNote = '$_basePath/ic_note.svg'; + static String icActivate = '$_basePath/ic_activate.svg'; + static String icAdd = '$_basePath/ic_add.svg'; + static String icBlock = '$_basePath/ic_block.svg'; + static String icContactGroup = '$_basePath/ic_contact_group.svg'; + static String icTrust = '$_basePath/ic_trust.svg'; + static String icBigTrust = '$_basePath/ic_big_trust.svg'; + static String icBigTrustActivated = '$_basePath/ic_big_trust_activated.svg'; + static String icTrash = '$_basePath/ic_trash.svg'; + static String icArrow = '$_basePath/ic_arrow.svg'; + static String icImage = '$_basePath/ic_image.svg'; } diff --git a/lib/view_models/add_contact_provider.dart b/lib/view_models/add_contact_provider.dart new file mode 100644 index 00000000..04df454e --- /dev/null +++ b/lib/view_models/add_contact_provider.dart @@ -0,0 +1,45 @@ +import 'package:at_contacts_flutter/services/contact_service.dart'; +import 'package:atsign_atmosphere_pro/view_models/base_model.dart'; + +class AddContactProvider extends BaseModel { + String addContactStatus = 'add_contact_status'; + ContactService contactService = ContactService(); + bool isVerify = false; + String atSignError = ''; + + void initData() { + contactService.resetData(); + isVerify = false; + } + + void changeVerifyStatus(bool verify) { + if (verify) atSignError = ''; + isVerify = verify; + notifyListeners(); + } + + Future addContact({ + required String atSign, + required String nickname, + }) async { + setStatus(addContactStatus, Status.Loading); + try { + await Future.delayed(Duration(seconds: 2)); + var response = await contactService.addAtSign( + atSign: atSign, + nickName: nickname, + ); + + if (response && (contactService.checkAtSign ?? false)) { + setStatus(addContactStatus, Status.Done); + return true; + } else { + atSignError = contactService.getAtSignError; + setStatus(addContactStatus, Status.Done); + } + } catch (e) { + setStatus(addContactStatus, Status.Error); + } + return null; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ae1a3982..3ac09218 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,7 +54,7 @@ dependencies: webview_flutter: ^3.0.4 http: ^0.13.4 package_info_plus: ^1.4.2 - fluttertoast: ^8.0.9 + fluttertoast: 8.1.2 file_selector: ^0.8.3 file_selector_macos: ^0.8.2 carousel_slider: ^4.0.0 @@ -85,9 +85,9 @@ dependency_overrides: biometric_storage: ^4.1.3 at_contacts_group_flutter: git: - url: https://github.com/atsign-foundation/at_widgets.git - path: at_contacts_group_flutter - ref: trunk + url: https://github.com/atsign-foundation/at_widgets + path: packages/at_contacts_group_flutter + ref: feature/new-version-contacts-group at_backupkey_flutter: git: url: https://github.com/atsign-foundation/at_widgets.git