Skip to content

Commit

Permalink
add a dropdown menu to choose tournament in a group and round
Browse files Browse the repository at this point in the history
  • Loading branch information
julien4215 committed Aug 29, 2024
1 parent 29eca30 commit c1ddc11
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 129 deletions.
35 changes: 27 additions & 8 deletions lib/src/model/broadcast/broadcast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Broadcast with _$Broadcast {
const Broadcast._();

const factory Broadcast({
required BroadcastTournament tour,
required BroadcastTournamentData tour,
required BroadcastRound round,
required String? group,

Expand All @@ -32,12 +32,26 @@ class Broadcast with _$Broadcast {
String get title => group ?? tour.name;
}

typedef BroadcastTournament = ({
String name,
String? imageUrl,
String description,
BroadcastTournamentInformation information,
});
@freezed
class BroadcastTournament with _$BroadcastTournament {
const factory BroadcastTournament({
required BroadcastTournamentData data,
required IList<BroadcastRound> rounds,
required BroadcastRoundId defaultRoundId,
required IList<BroadcastTournamentGroup>? group,
}) = _BroadcastTournament;
}

@freezed
class BroadcastTournamentData with _$BroadcastTournamentData {
const factory BroadcastTournamentData({
required BroadcastTournamentId id,
required String name,
required String? imageUrl,
required String description,
required BroadcastTournamentInformation information,
}) = _BroadcastTournamentData;
}

typedef BroadcastTournamentInformation = ({
String? format,
Expand All @@ -51,6 +65,11 @@ typedef BroadcastTournamentDates = ({
DateTime? endsAt,
});

typedef BroadcastTournamentGroup = ({
BroadcastTournamentId id,
String name,
});

@freezed
class BroadcastRound with _$BroadcastRound {
const BroadcastRound._();
Expand All @@ -59,7 +78,7 @@ class BroadcastRound with _$BroadcastRound {
required BroadcastRoundId id,
required String name,
required RoundStatus status,
required DateTime startsAt,
required DateTime? startsAt,
}) = _BroadcastRound;
}

Expand Down
22 changes: 22 additions & 0 deletions lib/src/model/broadcast/broadcast_providers.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast_repository.dart';
import 'package:lichess_mobile/src/model/common/http.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'broadcast_providers.g.dart';
Expand Down Expand Up @@ -40,3 +41,24 @@ class BroadcastsPaginator extends _$BroadcastsPaginator {
);
}
}

@riverpod
Future<BroadcastTournament> broadcastTournament(
BroadcastTournamentRef ref,
BroadcastTournamentId broadcastTournamentId,
) {
return ref.withClient(
(client) =>
BroadcastRepository(client).getTournament(broadcastTournamentId),
);
}

@riverpod
Future<BroadcastRoundGames> broadcastRound(
BroadcastRoundRef ref,
BroadcastRoundId broadcastRoundId,
) {
return ref.withClient(
(client) => BroadcastRepository(client).getRound(broadcastRoundId),
);
}
93 changes: 66 additions & 27 deletions lib/src/model/broadcast/broadcast_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ class BroadcastRepository {
);
}

Future<BroadcastTournament> getTournament(
BroadcastTournamentId broadcastTournamentId,
) {
return client.readJson(
Uri(path: 'api/broadcast/$broadcastTournamentId'),
headers: {'Accept': 'application/json'},
mapper: _makeTournamentFromJson,
);
}

Future<BroadcastRoundGames> getRound(
BroadcastRoundId broadcastRoundId,
) {
Expand Down Expand Up @@ -51,43 +61,72 @@ BroadcastsList _makeBroadcastResponseFromJson(
}

Broadcast _broadcastFromPick(RequiredPick pick) {
final live = pick('round', 'ongoing').asBoolOrFalse();
final finished = pick('round', 'finished').asBoolOrFalse();
final status = live
? RoundStatus.live
: finished
? RoundStatus.finished
: RoundStatus.upcoming;
final roundId = pick('round', 'id').asBroadcastRoundIdOrThrow();

return Broadcast(
tour: (
name: pick('tour', 'name').asStringOrThrow(),
imageUrl: pick('tour', 'image').asStringOrNull(),
description: pick('tour', 'description').asStringOrThrow(),
tour: _tournamentDataFromPick(pick('tour').required()),
round: _roundFromPick(pick('round').required()),
group: pick('group').asStringOrNull(),
roundToLinkId:
pick('roundToLink', 'id').asBroadcastRoundIddOrNull() ?? roundId,
);
}

BroadcastTournamentData _tournamentDataFromPick(
RequiredPick pick,
) =>
BroadcastTournamentData(
id: pick('id').asBroadcastTournamentIdOrThrow(),
name: pick('name').asStringOrThrow(),
imageUrl: pick('image').asStringOrNull(),
description: pick('description').asStringOrThrow(),
information: (
format: pick('tour', 'info', 'format').asStringOrNull(),
timeControl: pick('tour', 'info', 'tc').asStringOrNull(),
players: pick('tour', 'info', 'players').asStringOrNull(),
dates: pick('tour', 'dates').letOrNull(
format: pick('info', 'format').asStringOrNull(),
timeControl: pick('info', 'tc').asStringOrNull(),
players: pick('info', 'players').asStringOrNull(),
dates: pick('dates').letOrNull(
(pick) => (
startsAt: pick(0).asDateTimeFromMillisecondsOrThrow().toLocal(),
endsAt: pick(1).asDateTimeFromMillisecondsOrNull()?.toLocal(),
),
),
),
),
round: BroadcastRound(
id: roundId,
name: pick('round', 'name').asStringOrThrow(),
status: status,
startsAt: pick('round', 'startsAt')
.asDateTimeFromMillisecondsOrThrow()
.toLocal(),
),
group: pick('group').asStringOrNull(),
roundToLinkId:
pick('roundToLink', 'id').asBroadcastRoundIddOrNull() ?? roundId,
);

BroadcastTournament _makeTournamentFromJson(
Map<String, dynamic> json,
) {
return BroadcastTournament(
data: _tournamentDataFromPick(pick(json, 'tour').required()),
rounds: pick(json, 'rounds').asListOrThrow(_roundFromPick).toIList(),
defaultRoundId: pick(json, 'defaultRoundId').asBroadcastRoundIdOrThrow(),
group: pick(json, 'group', 'tours')
.asListOrNull(_tournamentGroupFromPick)
?.toIList(),
);
}

BroadcastTournamentGroup _tournamentGroupFromPick(RequiredPick pick) {
final id = pick('id').asBroadcastTournamentIdOrThrow();
final name = pick('name').asStringOrThrow();

return (id: id, name: name);
}

BroadcastRound _roundFromPick(RequiredPick pick) {
final live = pick('ongoing').asBoolOrFalse();
final finished = pick('finished').asBoolOrFalse();
final status = live
? RoundStatus.live
: finished
? RoundStatus.finished
: RoundStatus.upcoming;

return BroadcastRound(
id: pick('id').asBroadcastRoundIdOrThrow(),
name: pick('name').asStringOrThrow(),
status: status,
startsAt: pick('startsAt').asDateTimeFromMillisecondsOrNull()?.toLocal(),
);
}

Expand Down
11 changes: 6 additions & 5 deletions lib/src/model/broadcast/broadcast_round_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:dartchess/dartchess.dart';
import 'package:deep_pick/deep_pick.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart';
import 'package:lichess_mobile/src/model/broadcast/broadcast_repository.dart';
import 'package:lichess_mobile/src/model/common/chess.dart';
import 'package:lichess_mobile/src/model/common/http.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/common/socket.dart';
import 'package:lichess_mobile/src/utils/json.dart';
Expand All @@ -26,7 +26,7 @@ class BroadcastRoundController extends _$BroadcastRoundController {
@override
Future<BroadcastRoundGames> build(BroadcastRoundId broadcastRoundId) async {
_socketClient = ref
.read(socketPoolProvider)
.watch(socketPoolProvider)
.open(BroadcastRoundController.broadcastSocketUri(broadcastRoundId));

_subscription = _socketClient.stream.listen(_handleSocketEvent);
Expand All @@ -35,9 +35,10 @@ class BroadcastRoundController extends _$BroadcastRoundController {
_subscription?.cancel();
});

return await ref.withClient(
(client) => BroadcastRepository(client).getRound(broadcastRoundId),
);
final games =
await ref.watch(broadcastRoundProvider(broadcastRoundId).future);

return games;
}

void _handleSocketEvent(SocketEvent event) {
Expand Down
21 changes: 21 additions & 0 deletions lib/src/model/common/id.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ extension type const UserId(String value) implements StringId {

extension type const ChallengeId(String value) implements StringId {}

extension type const BroadcastTournamentId(String value) implements StringId {}

extension type const BroadcastRoundId(String value) implements StringId {}

extension type const BroadcastGameId(String value) implements StringId {}
Expand Down Expand Up @@ -149,6 +151,25 @@ extension IDPick on Pick {
}
}

BroadcastTournamentId asBroadcastTournamentIdOrThrow() {
final value = required().value;
if (value is String) {
return BroadcastTournamentId(value);
}
throw PickException(
"value $value at $debugParsingExit can't be casted to BroadcastRoundId",
);
}

BroadcastTournamentId? asBroadcastTournamentIdOrNull() {
if (value == null) return null;
try {
return asBroadcastTournamentIdOrThrow();
} catch (_) {
return null;
}
}

BroadcastRoundId asBroadcastRoundIdOrThrow() {
final value = required().value;
if (value is String) {
Expand Down
30 changes: 18 additions & 12 deletions lib/src/view/broadcast/broadcast_boards_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,29 @@ const _kPlayerWidgetPadding = EdgeInsets.symmetric(vertical: 5.0);
class BroadcastBoardsTab extends ConsumerWidget {
final BroadcastRoundId roundId;

const BroadcastBoardsTab(this.roundId);
const BroadcastBoardsTab({super.key, required this.roundId});

@override
Widget build(BuildContext context, WidgetRef ref) {
final games = ref.watch(broadcastRoundControllerProvider(roundId));

return games.when(
data: (games) => (games.isEmpty)
? const Text('No boards to show for now')
: BroadcastPreview(games: games.values.toIList()),
loading: () => const Shimmer(
child: ShimmerLoading(
isLoading: true,
child: BroadcastPreview(),
return SafeArea(
child: games.when(
data: (games) => (games.isEmpty)
? const Padding(
padding: Styles.bodyPadding,
child: Text('No boards to show for now'),
)
: BroadcastPreview(games: games.values.toIList()),
loading: () => const Shimmer(
child: ShimmerLoading(
isLoading: true,
child: BroadcastPreview(),
),
),
error: (error, stackTrace) => Center(
child: Text(error.toString()),
),
),
error: (error, stackTrace) => Center(
child: Text(error.toString()),
),
);
}
Expand Down Expand Up @@ -71,6 +76,7 @@ class BroadcastPreview extends StatelessWidget {
numberOfBoardsByRow;

return GridView.builder(
padding: Styles.bodyPadding,
itemCount: games == null ? numberLoadingBoards : games!.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: numberOfBoardsByRow,
Expand Down
Loading

0 comments on commit c1ddc11

Please sign in to comment.