Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions client/deepdame/lib/core/routing/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:deepdame/features/home/data/models/open_game.dart';
import 'package:deepdame/features/home/home_screen.dart';
import 'package:deepdame/features/home/lobby_screen.dart';
import 'package:deepdame/features/onboarding/onboarding_screen.dart';
import 'package:deepdame/features/opponent_profile/opponent_profile_screen.dart';
import 'package:deepdame/features/profile/profile_screen.dart';
import 'package:deepdame/features/settings/settings_screen.dart';
import 'package:deepdame/features/settings/theme_picker_screen.dart';
Expand Down Expand Up @@ -125,6 +126,14 @@ final appRouter = GoRouter(
path: '/user',
builder: (context, state) => const UserScreen(),
),
GoRoute(
name: 'opponent-profile',
path: '/opponent-profile/:userId',
builder: (context, state) {
final userId = state.pathParameters['userId']!;
return OpponentProfileScreen(userId: userId);
},
),
GoRoute(
name: 'settings',
path: '/settings',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'sent_requests_provider.g.dart';

@Riverpod(keepAlive: true)
class SentFriendRequests extends _$SentFriendRequests {
@override
Set<String> build() => {};

void add(String userId) {
state = {...state, userId};
}

bool contains(String userId) => state.contains(userId);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import 'package:deepdame/core/network/stomp_service.dart';
import 'package:deepdame/features/friends/data/models/user_search_result.dart';
import 'package:deepdame/features/friends/data/repositories/friends_repository.dart';
import 'package:deepdame/features/friends/presentation/providers/friends_provider.dart';
import 'package:deepdame/features/friends/presentation/providers/sent_requests_provider.dart';
import 'package:deepdame/shared/profile_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

class AddFriendSheet extends ConsumerStatefulWidget {
const AddFriendSheet({super.key});
Expand Down Expand Up @@ -70,7 +72,15 @@ class _AddFriendSheetState extends ConsumerState<AddFriendSheet> {
}

void _sendRequest(UserSearchResult result) {
ref.read(stompServiceProvider).send('/app/invite/friend/${result.id}');
final stomp = ref.read(stompServiceProvider);
if (!stomp.isConnected) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Connection lost. Try again.')),
);
return;
}
stomp.send('/app/invite/friend/${result.id}');
ref.read(sentFriendRequestsProvider.notifier).add(result.id);
setState(() {
_results = _results
.map(
Expand Down Expand Up @@ -252,16 +262,33 @@ class _AddFriendSheetState extends ConsumerState<AddFriendSheet> {
}

if (_results.isNotEmpty) {
final sentIds = ref.watch(sentFriendRequestsProvider);
final friendIds =
ref.watch(friendsProvider).asData?.value.map((f) => f.id).toSet() ??
{};
return ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 300),
child: ListView.builder(
shrinkWrap: true,
itemCount: _results.length,
itemBuilder: (_, i) => _SearchResultTile(
result: _results[i],
onAdd: () => _sendRequest(_results[i]),
onAccept: () => _acceptRequest(_results[i]),
),
itemBuilder: (_, i) {
final result = _results[i];
final effectiveRelationship = friendIds.contains(result.id)
? RelationshipStatus.friends
: result.relationship == RelationshipStatus.none &&
sentIds.contains(result.id)
? RelationshipStatus.requestSent
: result.relationship;
return _SearchResultTile(
result: result.copyWith(relationship: effectiveRelationship),
onAdd: () => _sendRequest(result),
onAccept: () => _acceptRequest(result),
onView: () => context.pushNamed(
'opponent-profile',
pathParameters: {'userId': result.id},
),
);
},
),
);
}
Expand Down Expand Up @@ -296,11 +323,13 @@ class _SearchResultTile extends StatelessWidget {
required this.result,
required this.onAdd,
required this.onAccept,
required this.onView,
});

final UserSearchResult result;
final VoidCallback onAdd;
final VoidCallback onAccept;
final VoidCallback onView;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -336,6 +365,29 @@ class _SearchResultTile extends StatelessWidget {
),
),
),
OutlinedButton(
onPressed: onView,
style: OutlinedButton.styleFrom(
foregroundColor: colorScheme.onSurface.withValues(alpha: 0.7),
side: BorderSide(
color: colorScheme.onSurface.withValues(alpha: 0.25),
),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
textStyle: textTheme.labelSmall?.copyWith(
fontWeight: FontWeight.w600,
),
),
child: const Text('View'),
),
const SizedBox(width: 6),
_RelationshipBadge(
relationship: result.relationship,
onAdd: onAdd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:deepdame/features/friends/presentation/providers/friends_provide
import 'package:deepdame/shared/profile_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

class FriendTile extends ConsumerStatefulWidget {
const FriendTile({
Expand Down Expand Up @@ -131,9 +132,10 @@ class _FriendTileState extends ConsumerState<FriendTile> {
}
}

void _onView() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Profile coming soon')),
Future<void> _onView() async {
await context.pushNamed(
'opponent-profile',
pathParameters: {'userId': widget.friend.id},
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';

part 'public_user_profile.freezed.dart';
part 'public_user_profile.g.dart';

@freezed
abstract class PublicUserProfile with _$PublicUserProfile {
const factory PublicUserProfile({
required String id,
required String username,
required bool online,
String? imageUrl,
}) = _PublicUserProfile;

factory PublicUserProfile.fromJson(Map<String, dynamic> json) =>
_$PublicUserProfileFromJson(json);
}
Loading
Loading