Commit ebb11608 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

fixes

parent a5ccb211
......@@ -74,6 +74,8 @@ PODS:
- GoogleUtilities/UserDefaults (~> 8.0)
- nanopb (~> 3.30910.0)
- Flutter (1.0.0)
- flutter_contacts (0.0.1):
- Flutter
- flutter_downloader (0.0.1):
- Flutter
- flutter_image_compress_common (1.0.0):
......@@ -212,6 +214,7 @@ DEPENDENCIES:
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
- flutter_downloader (from `.symlinks/plugins/flutter_downloader/ios`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
......@@ -277,6 +280,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_contacts:
:path: ".symlinks/plugins/flutter_contacts/ios"
flutter_downloader:
:path: ".symlinks/plugins/flutter_downloader/ios"
flutter_image_compress_common:
......@@ -342,6 +347,7 @@ SPEC CHECKSUMS:
FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3
FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
flutter_downloader: b7301ae057deadd4b1650dc7c05375f10ff12c39
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
flutter_inappwebview_ios: 8d8d2c6290a3c4787cad303603662fac9a788f75
......
......@@ -15,6 +15,7 @@
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
9AA45C142DD33732002B8BD0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */; };
9AE33D4D2EB6295300E40DCB /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AE33D4C2EB6295200E40DCB /* Contacts.framework */; };
9AED47FD9ED95B95B39F9EF0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */; };
C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
......@@ -67,6 +68,7 @@
9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
9AA45C152DD3487F002B8BD0 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
9AA45C162DD3489F002B8BD0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
9AE33D4C2EB6295200E40DCB /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
B9F0367623E7C0B4F78A4435 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
C968B3C3D5D358B0AFB94F8C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
......@@ -77,6 +79,7 @@
buildActionMask = 2147483647;
files = (
C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */,
9AE33D4D2EB6295300E40DCB /* Contacts.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -165,6 +168,7 @@
FE7E98E00E75972236D03914 /* Frameworks */ = {
isa = PBXGroup;
children = (
9AE33D4C2EB6295200E40DCB /* Contacts.framework */,
03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */,
790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */,
);
......
......@@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- here is ok or not this BELOW the URL types section, NOT inside it -->
<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts to add and view contacts.</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
......@@ -62,6 +58,8 @@
<string>This app requires to access your photo library in order to upload image proofs etc.</string>
<key>NSUserNotificationsUsageDescription</key>
<string>This app needs to access notifications.</string>
<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts to add and view contacts.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
......
......@@ -11,7 +11,6 @@ import 'package:permission_handler/permission_handler.dart';
import '../../Utils/custom_snackbar.dart';
class ContactListScreen extends StatefulWidget {
const ContactListScreen({super.key});
......@@ -25,10 +24,12 @@ class _ContactListScreenState extends State<ContactListScreen> {
@override
void initState() {
super.initState();
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
Future.microtask(() {
final provider = Provider.of<ContactProvider>(context, listen: false);
provider.fetchContactList(homeProv.session, homeProv.empId, 1);
WidgetsBinding.instance.addPostFrameCallback((_) {
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
Future.microtask(() {
final provider = Provider.of<ContactProvider>(context, listen: false);
provider.fetchContactList(homeProv.session, homeProv.empId, 1);
});
});
}
......@@ -47,23 +48,46 @@ class _ContactListScreenState extends State<ContactListScreen> {
context: context,
message: "Opening contact form for $name...",
);
// var permissionStatus = await Permission.contacts.request();
// // Check and request contact permission
// // var permissionStatus = await Permission.contacts.status;
// if (permissionStatus.isDenied) {
// final requestedStatus = await Permission.contacts.request();
// if (!requestedStatus.isGranted) {
// CustomSnackBar.hide(context);
// CustomSnackBar.showError(
// context: context,
// message: "Contact permission is required",
// );
// return;
// }
// }
// if (permissionStatus.isPermanentlyDenied) {
// CustomSnackBar.hide(context);
// _showPermissionDialog();
// return;
// }
// Check current status
var status = await Permission.contacts.status;
// Only request if not already granted
if (!await status.isGranted) {
status = await Permission.contacts.request();
}
// Check and request contact permission
var permissionStatus = await Permission.contacts.status;
if (permissionStatus.isDenied) {
final requestedStatus = await Permission.contacts.request();
if (!requestedStatus.isGranted) {
CustomSnackBar.hide(context);
CustomSnackBar.showError(
context: context,
message: "Contact permission is required",
);
return;
}
// Handle denied or permanently denied
if (await status.isDenied) {
CustomSnackBar.hide(context);
CustomSnackBar.showError(
context: context,
message: "Contact permission is required",
);
return;
}
if (permissionStatus.isPermanentlyDenied) {
if (await status.isPermanentlyDenied) {
CustomSnackBar.hide(context);
_showPermissionDialog();
return;
......@@ -73,9 +97,10 @@ class _ContactListScreenState extends State<ContactListScreen> {
final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber);
// ✅ Instead of inserting contact, open system form
final newContact = Contact()
..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)];
final newContact =
Contact()
..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)];
// 🟢 This opens the phone’s native “Add contact” screen (prefilled)
await FlutterContacts.openExternalInsert(newContact);
......@@ -88,7 +113,6 @@ class _ContactListScreenState extends State<ContactListScreen> {
context: context,
message: "Contact form opened for $name",
);
} catch (e) {
debugPrint("❌ Error opening contact form: $e");
CustomSnackBar.hide(context);
......@@ -122,24 +146,26 @@ class _ContactListScreenState extends State<ContactListScreen> {
void _showPermissionDialog() {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("Permission Required"),
content: const Text(
"Contact permission is permanently denied. Please enable it in app settings to add contacts."),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
openAppSettings();
},
child: const Text("Open Settings"),
builder:
(BuildContext context) => AlertDialog(
title: const Text("Permission Required"),
content: const Text(
"Contact permission is permanently denied. Please enable it in app settings to add contacts.",
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
openAppSettings();
},
child: const Text("Open Settings"),
),
],
),
],
),
);
}
......@@ -182,14 +208,24 @@ class _ContactListScreenState extends State<ContactListScreen> {
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: isSmallScreen ? 22 : isTablet ? 28 : 25,
height:
isSmallScreen
? 22
: isTablet
? 28
: 25,
),
),
const SizedBox(width: 10),
Text(
"Contacts",
style: TextStyle(
fontSize: isSmallScreen ? 16 : isTablet ? 20 : 18,
fontSize:
isSmallScreen
? 16
: isTablet
? 20
: 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: Colors.black87,
......@@ -210,18 +246,22 @@ class _ContactListScreenState extends State<ContactListScreen> {
child: Text(
"No contacts found",
style: TextStyle(
color: Colors.black54,
fontSize: 16,
fontFamily: "Plus Jakarta Sans"),
color: Colors.black54,
fontSize: 16,
fontFamily: "Plus Jakarta Sans",
),
),
);
}
final filteredContacts = provider.contactList
.where((c) => (c.name ?? '')
.toLowerCase()
.contains(_searchController.text.toLowerCase()))
.toList();
final filteredContacts =
provider.contactList
.where(
(c) => (c.name ?? '').toLowerCase().contains(
_searchController.text.toLowerCase(),
),
)
.toList();
return Padding(
padding: EdgeInsets.symmetric(horizontal: isSmallScreen ? 10 : 16),
......@@ -251,7 +291,8 @@ class _ContactListScreenState extends State<ContactListScreen> {
itemCount: filteredContacts.length,
itemBuilder: (context, index) {
final contact = filteredContacts[index];
final canSwipe = contact.mobileNumber != null &&
final canSwipe =
contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10;
return Padding(
......@@ -259,53 +300,68 @@ class _ContactListScreenState extends State<ContactListScreen> {
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Slidable(
key: Key(contact.mobileNumber?.toString() ?? '${contact.name}_$index'),
key: Key(
contact.mobileNumber?.toString() ??
'${contact.name}_$index',
),
// Left swipe (Add to contact)
startActionPane: canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
// This will automatically take full height of the list item
SlidableAction(
onPressed: (_) async {
_addToContact(
contact.name ?? 'Contact',
contact.mobileNumber ?? '',
);
},
backgroundColor: const Color(0xFFE5FFE5),
foregroundColor: const Color(0xFF38903c),
icon: Icons.contact_page,
label: 'Add to Contact',
autoClose: true,
),
],
)
: null,
startActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
// This will automatically take full height of the list item
SlidableAction(
onPressed: (_) async {
_addToContact(
contact.name ?? 'Contact',
contact.mobileNumber ?? '',
);
},
backgroundColor: const Color(
0xFFE5FFE5,
),
foregroundColor: const Color(
0xFF38903c,
),
icon: Icons.contact_page,
label: 'Add to Contact',
autoClose: true,
),
],
)
: null,
// Right swipe (WhatsApp)
endActionPane: canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) async {
_openWhatsApp(contact.mobileNumber ?? '');
},
backgroundColor: const Color(0xFFE9FFE8),
foregroundColor: const Color(0xFF4CB443),
icon: Icons.chat,
label: 'WhatsApp',
autoClose: true,
),
],
)
: null,
endActionPane:
canSwipe
? ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.4, // Width percentage
dragDismissible: false,
children: [
SlidableAction(
onPressed: (_) async {
_openWhatsApp(
contact.mobileNumber ?? '',
);
},
backgroundColor: const Color(
0xFFE9FFE8,
),
foregroundColor: const Color(
0xFF4CB443,
),
icon: Icons.chat,
label: 'WhatsApp',
autoClose: true,
),
],
)
: null,
// The main content - this determines the height
child: Container(
......@@ -314,7 +370,9 @@ class _ContactListScreenState extends State<ContactListScreen> {
borderRadius: BorderRadius.circular(1),
),
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 10),
horizontal: 14,
vertical: 10,
),
child: Row(
children: [
// 👤 Avatar
......@@ -326,23 +384,43 @@ class _ContactListScreenState extends State<ContactListScreen> {
shape: BoxShape.circle,
),
clipBehavior: Clip.antiAlias,
child: (contact.profileImage != null &&
contact.profileImage!.isNotEmpty)
? Image.network(
contact.profileImage!,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.person_outline,
color: Color(0xFF2d2d2d));
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(
child: CircularProgressIndicator(strokeWidth: 2),
);
},
)
: const Icon(Icons.person_outline, color: Color(0xFF2d2d2d)),
child:
(contact.profileImage != null &&
contact
.profileImage!
.isNotEmpty)
? Image.network(
contact.profileImage!,
fit: BoxFit.cover,
errorBuilder: (
context,
error,
stackTrace,
) {
return const Icon(
Icons.person_outline,
color: Color(0xFF2d2d2d),
);
},
loadingBuilder: (
context,
child,
loadingProgress,
) {
if (loadingProgress == null)
return child;
return const Center(
child:
CircularProgressIndicator(
strokeWidth: 2,
),
);
},
)
: const Icon(
Icons.person_outline,
color: Color(0xFF2d2d2d),
),
),
const SizedBox(width: 12),
......@@ -350,7 +428,8 @@ class _ContactListScreenState extends State<ContactListScreen> {
// 📝 Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
contact.name ?? "-",
......@@ -380,15 +459,25 @@ class _ContactListScreenState extends State<ContactListScreen> {
if (contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10)
IconButton(
icon: const Icon(Icons.call, color: Colors.green, size: 24),
icon: const Icon(
Icons.call,
color: Colors.green,
size: 24,
),
onPressed: () async {
final phone = contact.mobileNumber!.trim();
final Uri callUri = Uri(scheme: 'tel', path: phone);
final phone =
contact.mobileNumber!.trim();
final Uri callUri = Uri(
scheme: 'tel',
path: phone,
);
if (await canLaunchUrl(callUri)) {
await launchUrl(callUri);
} else {
debugPrint("❌ Could not launch dialer for $phone");
debugPrint(
"❌ Could not launch dialer for $phone",
);
CustomSnackBar.showError(
context: context,
message: "Unable to open dialer",
......@@ -412,4 +501,4 @@ class _ContactListScreenState extends State<ContactListScreen> {
),
);
}
}
\ No newline at end of file
}
......@@ -61,10 +61,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
auto_size_text:
dependency: "direct main"
description:
......@@ -445,10 +445,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
......@@ -1188,26 +1188,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "11.0.1"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.9"
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
lints:
dependency: transitive
description:
......@@ -2033,10 +2033,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
watcher:
dependency: transitive
description:
......
......@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.107+115
version: 1.0.107+116
environment:
sdk: ^3.7.2
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment