Appearance of the swipe card revised
parent
15a6556ac0
commit
3be7fa4dc1
|
@ -88,18 +88,36 @@ enum AvailabilityOption {
|
|||
lessThan10Hours,
|
||||
tenTo20Hours,
|
||||
twentyTo40Hours,
|
||||
fullTime;
|
||||
fullTime,
|
||||
flexible;
|
||||
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case AvailabilityOption.lessThan10Hours:
|
||||
return 'Less than 10 hours';
|
||||
case AvailabilityOption.tenTo20Hours:
|
||||
return 'Part time (10 - 20 hours)';
|
||||
return 'Part-time (10 - 20 hours)';
|
||||
case AvailabilityOption.twentyTo40Hours:
|
||||
return 'Part time (20 - 40 hours)';
|
||||
return 'Part-time (20 - 40 hours)';
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue