Appearance of the swipe card revised
parent
15a6556ac0
commit
3be7fa4dc1
|
@ -88,18 +88,36 @@ enum AvailabilityOption {
|
||||||
lessThan10Hours,
|
lessThan10Hours,
|
||||||
tenTo20Hours,
|
tenTo20Hours,
|
||||||
twentyTo40Hours,
|
twentyTo40Hours,
|
||||||
fullTime;
|
fullTime,
|
||||||
|
flexible;
|
||||||
|
|
||||||
String get displayName {
|
String get displayName {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case AvailabilityOption.lessThan10Hours:
|
case AvailabilityOption.lessThan10Hours:
|
||||||
return 'Less than 10 hours';
|
return 'Less than 10 hours';
|
||||||
case AvailabilityOption.tenTo20Hours:
|
case AvailabilityOption.tenTo20Hours:
|
||||||
return 'Part time (10 - 20 hours)';
|
return 'Part-time (10 - 20 hours)';
|
||||||
case AvailabilityOption.twentyTo40Hours:
|
case AvailabilityOption.twentyTo40Hours:
|
||||||
return 'Part time (20 - 40 hours)';
|
return 'Part-time (20 - 40 hours)';
|
||||||
case AvailabilityOption.fullTime:
|
case AvailabilityOption.fullTime:
|
||||||
return 'Full time (40 hours or more)';
|
return 'Full-time (40 hours or more)';
|
||||||
|
case AvailabilityOption.flexible:
|
||||||
|
return 'Flexible, full-time or part-time';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get commitmentText {
|
||||||
|
switch (this) {
|
||||||
|
case AvailabilityOption.lessThan10Hours:
|
||||||
|
return 'secondary occupation with less than 10 hours per week';
|
||||||
|
case AvailabilityOption.tenTo20Hours:
|
||||||
|
return 'part-time with 10 to 20 hours per week';
|
||||||
|
case AvailabilityOption.twentyTo40Hours:
|
||||||
|
return 'part-time with 20 to 40 hours per week';
|
||||||
|
case AvailabilityOption.fullTime:
|
||||||
|
return 'full-time';
|
||||||
|
case AvailabilityOption.flexible:
|
||||||
|
return 'full-time or part-time';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,10 @@ class MyLocation {
|
||||||
|
|
||||||
/// DE: Bundesland
|
/// DE: Bundesland
|
||||||
final String? administrativeArea;
|
final String? administrativeArea;
|
||||||
|
|
||||||
/// City
|
/// City
|
||||||
final String locality;
|
final String locality;
|
||||||
|
|
||||||
/// DE: Stadtteil
|
/// DE: Stadtteil
|
||||||
final String? subLocality;
|
final String? subLocality;
|
||||||
final String? postalCode;
|
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
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
if (locality.isNotEmpty && country.isNotEmpty) {
|
||||||
return '$locality, $country';
|
return '$locality, $country';
|
||||||
|
} else if (country.isNotEmpty) {
|
||||||
|
return country;
|
||||||
|
} else if (locality.isNotEmpty) {
|
||||||
|
return locality;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../components/my_drawer.dart';
|
import '../components/my_drawer.dart';
|
||||||
import '../services/auth/auth_service.dart';
|
|
||||||
|
|
||||||
class HomePage extends StatelessWidget {
|
class HomePage extends StatelessWidget {
|
||||||
HomePage({super.key});
|
const HomePage({super.key});
|
||||||
|
|
||||||
final AuthService _authService = AuthService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ class RegistrationCompletePage extends StatelessWidget {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => HomePage()),
|
MaterialPageRoute(builder: (context) => const HomePage()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Text('S T A R T'),
|
child: const Text('S T A R T'),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:swipable_stack/swipable_stack.dart';
|
import 'package:swipable_stack/swipable_stack.dart';
|
||||||
|
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
|
@ -69,7 +71,7 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
final Set<String> likedUserIds = swipesSnapshot.docs
|
final Set<String> likedUserIds = swipesSnapshot.docs
|
||||||
.where((doc) => doc['liked'] == true)
|
.where((doc) => doc[Constants.dbFieldSwipesLike] == true)
|
||||||
.map((doc) => doc.id)
|
.map((doc) => doc.id)
|
||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
|
@ -77,8 +79,10 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
||||||
DateTime.now().subtract(const Duration(hours: 24));
|
DateTime.now().subtract(const Duration(hours: 24));
|
||||||
final Set<String> dislikedUserIds = swipesSnapshot.docs
|
final Set<String> dislikedUserIds = swipesSnapshot.docs
|
||||||
.where((doc) =>
|
.where((doc) =>
|
||||||
doc['liked'] == false &&
|
doc[Constants.dbFieldSwipesLike] == false &&
|
||||||
(doc['timestamp'] as Timestamp).toDate().isAfter(thresholdDate))
|
(doc[Constants.dbFieldSwipesTimestamp] as Timestamp)
|
||||||
|
.toDate()
|
||||||
|
.isAfter(thresholdDate))
|
||||||
.map((doc) => doc.id)
|
.map((doc) => doc.id)
|
||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
|
@ -178,9 +182,9 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
||||||
.doc(swipedUserId) // UserID instead of autogenerated ID
|
.doc(swipedUserId) // UserID instead of autogenerated ID
|
||||||
.set(
|
.set(
|
||||||
{
|
{
|
||||||
'swipedId': swipedUserId,
|
Constants.dbFieldSwipesSwipedId: swipedUserId,
|
||||||
'liked': direction == SwipeDirection.right,
|
Constants.dbFieldSwipesLike: direction == SwipeDirection.right,
|
||||||
'timestamp': FieldValue.serverTimestamp(),
|
Constants.dbFieldSwipesTimestamp: FieldValue.serverTimestamp(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -197,8 +201,8 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
||||||
.collection(Constants.dbCollectionUsers)
|
.collection(Constants.dbCollectionUsers)
|
||||||
.doc(swipedUserId)
|
.doc(swipedUserId)
|
||||||
.collection(Constants.dbCollectionSwipes)
|
.collection(Constants.dbCollectionSwipes)
|
||||||
.where('swipedId', isEqualTo: currentUserId)
|
.where(Constants.dbFieldSwipesSwipedId, isEqualTo: currentUserId)
|
||||||
.where('liked', isEqualTo: true)
|
.where(Constants.dbFieldSwipesLike, isEqualTo: true)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (matchSnapshot.docs.isNotEmpty) {
|
if (matchSnapshot.docs.isNotEmpty) {
|
||||||
|
@ -384,32 +388,105 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUserCard(UserProfile userProfile) {
|
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(
|
return Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(userProfile.name, style: const TextStyle(fontSize: 24)),
|
if (kDebugMode)
|
||||||
Text(userProfile.email, style: const TextStyle(fontSize: 24)),
|
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),
|
const SizedBox(height: 8),
|
||||||
Text(
|
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(
|
Text(
|
||||||
'Seeks someone with skills in: ${userProfile.skillsSought.map((x) => x.displayName).join(', ')}'),
|
'He/She brings skills and experience in '
|
||||||
Text('Availability: ${userProfile.availability.displayName}'),
|
'${userProfile.skills.map((x) => x.displayName).join(', ')}',
|
||||||
Text('Risk type: ${userProfile.risk.displayName}'),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Speaks: ${userProfile.languages.map((lang) => lang.name).join(', ')}'),
|
'and is willing to commit in '
|
||||||
|
'${userProfile.availability.commitmentText}.',
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
'Lives in: ${userProfile.locations[Constants.dbDocMainLocation]?.locality ?? 'N/A'}'),
|
'Lives in ${userProfile.locations[Constants.dbDocMainLocation]?.toString() ?? 'N/A'}'
|
||||||
Text(
|
' and ${userProfile.locations[Constants.dbDocSecondLocation]?.toString() ?? 'N/A'}'
|
||||||
'Coordinates: ${userProfile.locations[Constants.dbDocMainLocation]?.latitude} ${userProfile.locations[Constants.dbDocMainLocation]?.longitude}'),
|
' which is only/about $shortDist km away from you.',
|
||||||
Text(
|
),
|
||||||
'Second home: ${userProfile.locations[Constants.dbDocSecondLocation]?.locality ?? 'N/A'}'),
|
const SizedBox(height: 8),
|
||||||
Text(
|
const Row(
|
||||||
'Shortest distance: ${shortestDistanceBetweenUsers(currentUserProfile!, userProfile).toStringAsFixed(0)} km'),
|
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(
|
child: Icon(
|
||||||
direction == SwipeDirection.right
|
direction == SwipeDirection.right
|
||||||
? Icons.thumb_up
|
? Icons.thumb_up
|
||||||
: (direction == SwipeDirection.left
|
: (direction == SwipeDirection.left ? Icons.thumb_down : null),
|
||||||
? Icons.thumb_down
|
|
||||||
: Icons.skip_next),
|
|
||||||
size: 100,
|
size: 100,
|
||||||
color: direction == SwipeDirection.right
|
color: direction == SwipeDirection.right
|
||||||
? Colors.green
|
? Colors.green
|
||||||
|
|
|
@ -34,7 +34,7 @@ class AuthGate extends StatelessWidget {
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
} else if (snapshot.hasData && snapshot.data == true) {
|
} else if (snapshot.hasData && snapshot.data == true) {
|
||||||
return HomePage();
|
return const HomePage();
|
||||||
} else {
|
} else {
|
||||||
// also in case of (snapshot.hasError)
|
// also in case of (snapshot.hasError)
|
||||||
return const UserDataPage(
|
return const UserDataPage(
|
||||||
|
|
Loading…
Reference in New Issue