Skip to content

Commit

Permalink
Merge pull request #142 from canopas/Mayank/select-team-via-qr-code
Browse files Browse the repository at this point in the history
Select team via qr code
  • Loading branch information
cp-mayank authored Nov 21, 2024
2 parents 17048f5 + 8fcf2e9 commit f33b400
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 57 deletions.
1 change: 1 addition & 0 deletions khelo/assets/locales/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
"add_team_player_role_title": "Playing role",
"add_team_player_add_via_contact_title": "Add via Contact",
"add_team_players_text": "Players",
"add_team_already_added": "Team already added",
"add_team_add_hint_text": "Added user will be shown here, tap on '+' button to add team member.",

"scan_qr_code": "Scan QR code",
Expand Down
27 changes: 20 additions & 7 deletions khelo/lib/ui/app_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'package:khelo/ui/flow/team/add_team_member/contact_selection/contact_sel
import 'package:khelo/ui/flow/team/detail/make_admin/make_team_admin_screen.dart';
import 'package:khelo/ui/flow/team/detail/team_detail_screen.dart';
import 'package:khelo/ui/flow/team/scanner/scanner_screen.dart';
import 'package:khelo/ui/flow/team/scanner/scanner_view_model.dart';
import 'package:khelo/ui/flow/team/search_team/search_team_screen.dart';
import 'package:khelo/ui/flow/tournament/add/add_tournament_screen.dart';
import 'package:khelo/ui/flow/tournament/detail/members/tournament_detail_members_screen.dart';
Expand Down Expand Up @@ -210,9 +211,9 @@ class AppRoute {
);

static AppRoute matchSelection({required String tournamentId}) => AppRoute(
pathMatchSelection,
builder: (_) => MatchSelectionScreen(tournamentId: tournamentId),
);
pathMatchSelection,
builder: (_) => MatchSelectionScreen(tournamentId: tournamentId),
);

static AppRoute tournamentDetail({required String tournamentId}) => AppRoute(
pathTournamentDetail,
Expand Down Expand Up @@ -301,9 +302,15 @@ class AppRoute {
pathEditProfile,
builder: (_) => EditProfileScreen(isToCreateAccount: isToCreateAccount));

static AppRoute teamDetail({required String teamId}) =>
static AppRoute teamDetail({
required String teamId,
bool showAddButton = false,
}) =>
AppRoute(pathTeamDetail,
builder: (_) => TeamDetailScreen(teamId: teamId));
builder: (_) => TeamDetailScreen(
teamId: teamId,
showAddButton: showAddButton,
));

static AppRoute userDetail({
required String userId,
Expand All @@ -315,10 +322,16 @@ class AppRoute {
showAddButton: showAddButton,
));

static AppRoute scannerScreen({required List<String> addedMembers}) =>
static AppRoute scannerScreen({
required List<String> addedIds,
ScanTarget target = ScanTarget.player,
}) =>
AppRoute(
pathScannerScreen,
builder: (_) => ScannerScreen(addedMembers: addedMembers),
builder: (_) => ScannerScreen(
addedIds: addedIds,
target: target,
),
);

static AppRoute qrCodeView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class _AddTeamMemberScreenState extends ConsumerState<AddTeamMemberScreen> {
actionButton(
context,
onPressed: () async {
final user = await AppRoute.scannerScreen(
addedMembers: notifier.getMemberIds())
.push<UserModel>(context);
final user =
await AppRoute.scannerScreen(addedIds: notifier.getMemberIds())
.push<UserModel>(context);
if (context.mounted && user != null) notifier.selectUser(user);
},
icon: SvgPicture.asset(
Expand Down
30 changes: 21 additions & 9 deletions khelo/lib/ui/flow/team/detail/team_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:khelo/ui/flow/team/detail/components/team_detail_member_content.
import 'package:khelo/ui/flow/team/detail/team_detail_view_model.dart';
import 'package:style/button/action_button.dart';
import 'package:style/button/more_option_button.dart';
import 'package:style/button/secondary_button.dart';
import 'package:style/button/tab_button.dart';
import 'package:style/extensions/context_extensions.dart';
import 'package:style/indicator/progress_indicator.dart';
Expand All @@ -25,8 +26,13 @@ import 'components/team_detail_stat_content.dart';

class TeamDetailScreen extends ConsumerStatefulWidget {
final String teamId;
final bool showAddButton;

const TeamDetailScreen({super.key, required this.teamId});
const TeamDetailScreen({
super.key,
required this.teamId,
required this.showAddButton,
});

@override
ConsumerState createState() => _TeamDetailScreenState();
Expand Down Expand Up @@ -66,14 +72,20 @@ class _TeamDetailScreenState extends ConsumerState<TeamDetailScreen> {
size: 24,
color: context.colorScheme.textPrimary,
)),
actions: (state.team?.isAdminOrOwner(state.currentUserId) ?? false)
? [
moreOptionButton(
context,
onPressed: () => _moreActionButton(context, notifier, state),
),
]
: null,
actions: [
if (widget.showAddButton)
SecondaryButton(
context.l10n.common_add_title,
enabled: !state.loading,
onPressed: () => context.pop(state.team),
),
if (!widget.showAddButton &&
(state.team?.isAdminOrOwner(state.currentUserId) ?? false))
moreOptionButton(
context,
onPressed: () => _moreActionButton(context, notifier, state),
),
],
body: Builder(builder: (context) {
return _body(context, state);
}),
Expand Down
50 changes: 36 additions & 14 deletions khelo/lib/ui/flow/team/scanner/scanner_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:math';

import 'package:data/api/team/team_model.dart';
import 'package:data/api/user/user_models.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand All @@ -22,9 +23,14 @@ import '../../../../components/app_page.dart';
import '../../../../components/error_snackbar.dart';

class ScannerScreen extends ConsumerStatefulWidget {
final List<String> addedMembers;
final List<String> addedIds;
final ScanTarget target;

const ScannerScreen({super.key, required this.addedMembers});
const ScannerScreen({
super.key,
required this.addedIds,
this.target = ScanTarget.player,
});

@override
ConsumerState<ScannerScreen> createState() => _ScannerScreenState();
Expand All @@ -36,21 +42,37 @@ class _ScannerScreenState extends ConsumerState<ScannerScreen> {
late final ScannerStateNotifier _notifier;

void _observeBarcode() {
ref.listen(scannerStateNotifierProvider.select((value) => value.userId),
(prev, userId) async {
if (widget.addedMembers.contains(userId)) {
showSnackBar(context, context.l10n.add_team_member_already_added);
} else if (userId.isNotEmpty) {
final user =
await AppRoute.userDetail(userId: userId, showAddButton: true)
.push<UserModel?>(context);
if (mounted) {
context.pop(user);
}
ref.listen(scannerStateNotifierProvider.select((value) => value.scannedId),
(prev, scannedId) async {
if (widget.addedIds.contains(scannedId)) {
showSnackBar(
context,
widget.target == ScanTarget.team
? context.l10n.add_team_already_added
: context.l10n.add_team_member_already_added);
} else if (scannedId.isNotEmpty) {
_handleScanTarget(widget.target, scannedId);
}
});
}

void _handleScanTarget(ScanTarget target, String scannedId) async {
switch (widget.target) {
case ScanTarget.team:
final team =
await AppRoute.teamDetail(teamId: scannedId, showAddButton: true)
.push<TeamModel>(context);
if (mounted) context.pop(team);
break;
case ScanTarget.player:
final user =
await AppRoute.userDetail(userId: scannedId, showAddButton: true)
.push<UserModel?>(context);
if (mounted) context.pop(user);
break;
}
}

void _observeError() {
ref.listen(scannerStateNotifierProvider.select((value) => value.error),
(prev, error) {
Expand All @@ -63,7 +85,7 @@ class _ScannerScreenState extends ConsumerState<ScannerScreen> {
@override
void initState() {
_notifier = ref.read(scannerStateNotifierProvider.notifier);
runPostFrame(() => _notifier.setData(widget.addedMembers));
runPostFrame(() => _notifier.setData(widget.addedIds));
super.initState();
}

Expand Down
14 changes: 8 additions & 6 deletions khelo/lib/ui/flow/team/scanner/scanner_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ final scannerStateNotifierProvider =
});

class ScannerStateNotifier extends StateNotifier<ScannerState> {
List<String> _addedMembers = [];
List<String> _addedIds = [];
StreamSubscription? _subscription;

ScannerStateNotifier() : super(const ScannerState()) {
_checkCameraPermission();
}

void setData(List<String> addedMembers) {
_addedMembers = addedMembers;
void setData(List<String> addedIds) {
_addedIds = addedIds;
}

Future<void> _checkCameraPermission() async {
Expand Down Expand Up @@ -62,11 +62,11 @@ class ScannerStateNotifier extends StateNotifier<ScannerState> {
_subscription = state.controller?.scannedDataStream.listen(
(event) {
final code = event.code;
if (!_addedMembers.contains(code)) {
if (!_addedIds.contains(code)) {
_removeListeners();
}
state = state.copyWith(
userId: code ?? "",
scannedId: code ?? "",
);
},
onError: (e) {
Expand All @@ -93,8 +93,10 @@ class ScannerState with _$ScannerState {
const factory ScannerState({
Object? error,
QRViewController? controller,
@Default('') String userId,
@Default('') String scannedId,
@Default(false) bool flashOn,
@Default(false) bool hasPermission,
}) = _ScannerState;
}

enum ScanTarget { player, team }
37 changes: 19 additions & 18 deletions khelo/lib/ui/flow/team/scanner/scanner_view_model.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final _privateConstructorUsedError = UnsupportedError(
mixin _$ScannerState {
Object? get error => throw _privateConstructorUsedError;
QRViewController? get controller => throw _privateConstructorUsedError;
String get userId => throw _privateConstructorUsedError;
String get scannedId => throw _privateConstructorUsedError;
bool get flashOn => throw _privateConstructorUsedError;
bool get hasPermission => throw _privateConstructorUsedError;

Expand All @@ -38,7 +38,7 @@ abstract class $ScannerStateCopyWith<$Res> {
$Res call(
{Object? error,
QRViewController? controller,
String userId,
String scannedId,
bool flashOn,
bool hasPermission});
}
Expand All @@ -60,7 +60,7 @@ class _$ScannerStateCopyWithImpl<$Res, $Val extends ScannerState>
$Res call({
Object? error = freezed,
Object? controller = freezed,
Object? userId = null,
Object? scannedId = null,
Object? flashOn = null,
Object? hasPermission = null,
}) {
Expand All @@ -70,9 +70,9 @@ class _$ScannerStateCopyWithImpl<$Res, $Val extends ScannerState>
? _value.controller
: controller // ignore: cast_nullable_to_non_nullable
as QRViewController?,
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
scannedId: null == scannedId
? _value.scannedId
: scannedId // ignore: cast_nullable_to_non_nullable
as String,
flashOn: null == flashOn
? _value.flashOn
Expand All @@ -97,7 +97,7 @@ abstract class _$$ScannerStateImplCopyWith<$Res>
$Res call(
{Object? error,
QRViewController? controller,
String userId,
String scannedId,
bool flashOn,
bool hasPermission});
}
Expand All @@ -117,7 +117,7 @@ class __$$ScannerStateImplCopyWithImpl<$Res>
$Res call({
Object? error = freezed,
Object? controller = freezed,
Object? userId = null,
Object? scannedId = null,
Object? flashOn = null,
Object? hasPermission = null,
}) {
Expand All @@ -127,9 +127,9 @@ class __$$ScannerStateImplCopyWithImpl<$Res>
? _value.controller
: controller // ignore: cast_nullable_to_non_nullable
as QRViewController?,
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
scannedId: null == scannedId
? _value.scannedId
: scannedId // ignore: cast_nullable_to_non_nullable
as String,
flashOn: null == flashOn
? _value.flashOn
Expand All @@ -149,7 +149,7 @@ class _$ScannerStateImpl implements _ScannerState {
const _$ScannerStateImpl(
{this.error,
this.controller,
this.userId = '',
this.scannedId = '',
this.flashOn = false,
this.hasPermission = false});

Expand All @@ -159,7 +159,7 @@ class _$ScannerStateImpl implements _ScannerState {
final QRViewController? controller;
@override
@JsonKey()
final String userId;
final String scannedId;
@override
@JsonKey()
final bool flashOn;
Expand All @@ -169,7 +169,7 @@ class _$ScannerStateImpl implements _ScannerState {

@override
String toString() {
return 'ScannerState(error: $error, controller: $controller, userId: $userId, flashOn: $flashOn, hasPermission: $hasPermission)';
return 'ScannerState(error: $error, controller: $controller, scannedId: $scannedId, flashOn: $flashOn, hasPermission: $hasPermission)';
}

@override
Expand All @@ -180,7 +180,8 @@ class _$ScannerStateImpl implements _ScannerState {
const DeepCollectionEquality().equals(other.error, error) &&
(identical(other.controller, controller) ||
other.controller == controller) &&
(identical(other.userId, userId) || other.userId == userId) &&
(identical(other.scannedId, scannedId) ||
other.scannedId == scannedId) &&
(identical(other.flashOn, flashOn) || other.flashOn == flashOn) &&
(identical(other.hasPermission, hasPermission) ||
other.hasPermission == hasPermission));
Expand All @@ -191,7 +192,7 @@ class _$ScannerStateImpl implements _ScannerState {
runtimeType,
const DeepCollectionEquality().hash(error),
controller,
userId,
scannedId,
flashOn,
hasPermission);

Expand All @@ -208,7 +209,7 @@ abstract class _ScannerState implements ScannerState {
const factory _ScannerState(
{final Object? error,
final QRViewController? controller,
final String userId,
final String scannedId,
final bool flashOn,
final bool hasPermission}) = _$ScannerStateImpl;

Expand All @@ -217,7 +218,7 @@ abstract class _ScannerState implements ScannerState {
@override
QRViewController? get controller;
@override
String get userId;
String get scannedId;
@override
bool get flashOn;
@override
Expand Down
Loading

0 comments on commit f33b400

Please sign in to comment.