Appearance of the swipe card revised

master
Rafael 2024-06-09 00:04:13 +02:00
parent 15a6556ac0
commit e2096b4989
5 changed files with 165 additions and 83 deletions

View File

@ -8,8 +8,10 @@ class MyLocation {
/// DE: Bundesland
final String? administrativeArea;
/// City
final String locality;
/// DE: Stadtteil
final String? subLocality;
final String? postalCode;
@ -67,10 +69,18 @@ class MyLocation {
}
}
/// Returns: locality, country
/// Returns the location formatted as 'locality, country'
/// if both values are present or an empty string if both values are empty.
@override
String toString() {
return '$locality, $country';
if (locality.isNotEmpty && country.isNotEmpty) {
return '$locality, $country';
} else if (country.isNotEmpty) {
return country;
} else if (locality.isNotEmpty) {
return locality;
}
return '';
}
@override

View File

@ -1,12 +1,9 @@
import 'package:flutter/material.dart';
import '../components/my_drawer.dart';
import '../services/auth/auth_service.dart';
class HomePage extends StatelessWidget {
HomePage({super.key});
final AuthService _authService = AuthService();
const HomePage({super.key});
@override
Widget build(BuildContext context) {
@ -19,58 +16,58 @@ class HomePage extends StatelessWidget {
drawer: const MyDrawer(),
// body: _buildUserList(),
body: Center(
child: AspectRatio(
aspectRatio: 1,
child: LayoutBuilder(
builder: (context, constraints) {
// Calculation of the tile size based on the spacing
double size = constraints.biggest.shortestSide / 2 - 16;
return GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
padding: const EdgeInsets.all(16.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildTile(
context,
'Discover',
Icons.explore,
Colors.lightBlue.shade300,
'/discover',
size,
),
_buildTile(
context,
'View Profiles',
Icons.supervised_user_circle,
Colors.teal.shade300,
'/favorites',
size,
),
_buildTile(
context,
'Chat',
Icons.chat,
Colors.indigo.shade300,
'/chats',
size,
),
_buildTile(
context,
'My Profile',
Icons.edit_note,
Colors.blueGrey.shade300,
'/profile',
size,
),
],
);
},
),
child: AspectRatio(
aspectRatio: 1,
child: LayoutBuilder(
builder: (context, constraints) {
// Calculation of the tile size based on the spacing
double size = constraints.biggest.shortestSide / 2 - 16;
return GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
padding: const EdgeInsets.all(16.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildTile(
context,
'Discover',
Icons.explore,
Colors.lightBlue.shade300,
'/discover',
size,
),
_buildTile(
context,
'View Profiles',
Icons.supervised_user_circle,
Colors.teal.shade300,
'/favorites',
size,
),
_buildTile(
context,
'Chat',
Icons.chat,
Colors.indigo.shade300,
'/chats',
size,
),
_buildTile(
context,
'My Profile',
Icons.edit_note,
Colors.blueGrey.shade300,
'/profile',
size,
),
],
);
},
),
),
),
);
}

View File

@ -45,7 +45,7 @@ class RegistrationCompletePage extends StatelessWidget {
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
MaterialPageRoute(builder: (context) => const HomePage()),
);
},
child: const Text('S T A R T'),

View File

@ -1,6 +1,8 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:swipable_stack/swipable_stack.dart';
import '../constants.dart';
@ -69,7 +71,7 @@ class UserMatchingPageState extends State<UserMatchingPage> {
.get();
final Set<String> likedUserIds = swipesSnapshot.docs
.where((doc) => doc['liked'] == true)
.where((doc) => doc[Constants.dbFieldSwipesLike] == true)
.map((doc) => doc.id)
.toSet();
@ -77,8 +79,10 @@ class UserMatchingPageState extends State<UserMatchingPage> {
DateTime.now().subtract(const Duration(hours: 24));
final Set<String> dislikedUserIds = swipesSnapshot.docs
.where((doc) =>
doc['liked'] == false &&
(doc['timestamp'] as Timestamp).toDate().isAfter(thresholdDate))
doc[Constants.dbFieldSwipesLike] == false &&
(doc[Constants.dbFieldSwipesTimestamp] as Timestamp)
.toDate()
.isAfter(thresholdDate))
.map((doc) => doc.id)
.toSet();
@ -178,9 +182,9 @@ class UserMatchingPageState extends State<UserMatchingPage> {
.doc(swipedUserId) // UserID instead of autogenerated ID
.set(
{
'swipedId': swipedUserId,
'liked': direction == SwipeDirection.right,
'timestamp': FieldValue.serverTimestamp(),
Constants.dbFieldSwipesSwipedId: swipedUserId,
Constants.dbFieldSwipesLike: direction == SwipeDirection.right,
Constants.dbFieldSwipesTimestamp: FieldValue.serverTimestamp(),
},
);
}
@ -197,8 +201,8 @@ class UserMatchingPageState extends State<UserMatchingPage> {
.collection(Constants.dbCollectionUsers)
.doc(swipedUserId)
.collection(Constants.dbCollectionSwipes)
.where('swipedId', isEqualTo: currentUserId)
.where('liked', isEqualTo: true)
.where(Constants.dbFieldSwipesSwipedId, isEqualTo: currentUserId)
.where(Constants.dbFieldSwipesLike, isEqualTo: true)
.get();
if (matchSnapshot.docs.isNotEmpty) {
@ -384,32 +388,105 @@ class UserMatchingPageState extends State<UserMatchingPage> {
}
Widget _buildUserCard(UserProfile userProfile) {
String? profileImageUrl = userProfile.profilePictureUrl;
int age = calcAge(userProfile.born);
String ageInfo = age > 0 ? ' ($age)' : '';
// Sort the languages according to the given order below
List<Language> sortedLanguages = List.from(userProfile.languages);
sortedLanguages.sort((a, b) {
if (a.code == 'de') return -1; // German first
if (b.code == 'de') return 1; // German first
if (a.code == 'en') return -1; // English second
if (b.code == 'en') return 1; // English second
return a.name.compareTo(b.name); // All others by name ascending
});
String shortDist =
shortestDistanceBetweenUsers(currentUserProfile!, userProfile)
.toStringAsFixed(0);
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userProfile.name, style: const TextStyle(fontSize: 24)),
Text(userProfile.email, style: const TextStyle(fontSize: 24)),
if (kDebugMode)
Center(
child: Text(
userProfile.email,
style: const TextStyle(fontSize: 12),
),
),
Center(
child: CircleAvatar(
radius: 50,
backgroundImage:
((profileImageUrl != null && profileImageUrl.isNotEmpty))
? NetworkImage(profileImageUrl)
: null,
child: (profileImageUrl == null || profileImageUrl.isEmpty)
? const Icon(Icons.person_pin, size: 50)
: null,
),
),
const SizedBox(height: 8),
Center(
child: Text('${userProfile.name}$ageInfo',
style: const TextStyle(fontSize: 24)),
),
const SizedBox(height: 8),
Text(
'Has skills and experience in: ${userProfile.skills.map((x) => x.displayName).join(', ')}'),
'Would like to team up with someone who has experience in '
'${userProfile.skillsSought.map((x) => x.displayName).join(', ')}.',
),
Text(
'Seeks someone with skills in: ${userProfile.skillsSought.map((x) => x.displayName).join(', ')}'),
Text('Availability: ${userProfile.availability.displayName}'),
Text('Risk type: ${userProfile.risk.displayName}'),
'He/She brings skills and experience in '
'${userProfile.skills.map((x) => x.displayName).join(', ')}',
),
Text(
'Speaks: ${userProfile.languages.map((lang) => lang.name).join(', ')}'),
'and is willing to commit in '
'${userProfile.availability.commitmentText}.',
),
Text(
'Lives in: ${userProfile.locations[Constants.dbDocMainLocation]?.locality ?? 'N/A'}'),
Text(
'Coordinates: ${userProfile.locations[Constants.dbDocMainLocation]?.latitude} ${userProfile.locations[Constants.dbDocMainLocation]?.longitude}'),
Text(
'Second home: ${userProfile.locations[Constants.dbDocSecondLocation]?.locality ?? 'N/A'}'),
Text(
'Shortest distance: ${shortestDistanceBetweenUsers(currentUserProfile!, userProfile).toStringAsFixed(0)} km'),
'Lives in ${userProfile.locations[Constants.dbDocMainLocation]?.toString() ?? 'N/A'}'
' and ${userProfile.locations[Constants.dbDocSecondLocation]?.toString() ?? 'N/A'}'
' which is only/about $shortDist km away from you.',
),
const SizedBox(height: 8),
const Row(
children: [
Text('Spoken languages '),
Expanded(child: Divider()),
],
),
const SizedBox(height: 4),
Wrap(
children: sortedLanguages.map(
(language) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
language.iconFile,
height: 12.0,
),
const SizedBox(width: 4.0),
Text(language.name),
// Space between each language icon pair
const SizedBox(width: 8.0),
],
);
},
).toList(),
),
//Text('Risk type: ${userProfile.risk.displayName}'),
],
),
),
@ -554,9 +631,7 @@ class CardOverlay extends StatelessWidget {
child: Icon(
direction == SwipeDirection.right
? Icons.thumb_up
: (direction == SwipeDirection.left
? Icons.thumb_down
: Icons.skip_next),
: (direction == SwipeDirection.left ? Icons.thumb_down : null),
size: 100,
color: direction == SwipeDirection.right
? Colors.green

View File

@ -34,7 +34,7 @@ class AuthGate extends StatelessWidget {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData && snapshot.data == true) {
return HomePage();
return const HomePage();
} else {
// also in case of (snapshot.hasError)
return const UserDataPage(