69 changed files with 1180 additions and 497 deletions
-
16android/app/build.gradle.kts
-
18lib/core/constants/my_api.dart
-
5lib/core/params/battle_league_params.dart
-
14lib/core/params/bl_params.dart
-
22lib/core/routers/my_routes.dart
-
176lib/core/services/web_socket_service.dart
-
71lib/features/battle_league/data/datasource/battle_league_datasource.dart
-
9lib/features/battle_league/data/model/answer_model.dart
-
17lib/features/battle_league/data/model/clock_model.dart
-
23lib/features/battle_league/data/model/match_making_model.dart
-
13lib/features/battle_league/data/model/player_token_model.dart
-
18lib/features/battle_league/data/model/question_model.dart
-
2lib/features/battle_league/data/model/topics_model.dart
-
94lib/features/battle_league/data/repository_impl/battle_league_repository_impl.dart
-
11lib/features/battle_league/domain/entity/answer_entity.dart
-
12lib/features/battle_league/domain/entity/clock_entity.dart
-
29lib/features/battle_league/domain/entity/match_making_entity.dart
-
12lib/features/battle_league/domain/entity/player_token_entity.dart
-
14lib/features/battle_league/domain/entity/question_entity.dart
-
0lib/features/battle_league/domain/entity/topics_entity.dart
-
22lib/features/battle_league/domain/repository/battle_league_repository.dart
-
17lib/features/battle_league/domain/usecases/get_clock_usecase.dart
-
18lib/features/battle_league/domain/usecases/get_player_token_usecase.dart
-
18lib/features/battle_league/domain/usecases/get_topics_usecase.dart
-
17lib/features/battle_league/domain/usecases/match_making_usecase.dart
-
28lib/features/battle_league/first_part/data/datasource/battle_league_datasource.dart
-
14lib/features/battle_league/first_part/data/model/battle_league_model.dart
-
33lib/features/battle_league/first_part/data/repository_impl/battle_league_repository_impl.dart
-
15lib/features/battle_league/first_part/domain/entity/battle_league_entity.dart
-
10lib/features/battle_league/first_part/domain/repository/battle_league_repository.dart
-
18lib/features/battle_league/first_part/domain/usecases/get_topics_usecase.dart
-
87lib/features/battle_league/first_part/presentation/controller/battle_league_controller.dart
-
9lib/features/battle_league/presentation/binding/battle_league_binding.dart
-
280lib/features/battle_league/presentation/controller/battle_league_controller.dart
-
45lib/features/battle_league/presentation/ui/first_part/battle_league_finding_page.dart
-
6lib/features/battle_league/presentation/ui/first_part/battle_league_founded_page.dart
-
10lib/features/battle_league/presentation/ui/first_part/battle_league_page.dart
-
8lib/features/battle_league/presentation/ui/first_part/battle_league_topic_page.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/button/battle_golden_button.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/button/battle_grey_button.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/button/battle_purple_button.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/founded_page/founded_avatar.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/battle_league_tab_bar.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/filter_ranking_button.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/my_ranking_widget.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/ranking_region.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/ranking_scrollbar.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/ranking_time.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/main_page/ranking_widget.dart
-
10lib/features/battle_league/presentation/ui/first_part/widgets/main_page/regional_ranking.dart
-
10lib/features/battle_league/presentation/ui/first_part/widgets/main_page/time_ranking.dart
-
0lib/features/battle_league/presentation/ui/first_part/widgets/rank_title.dart
-
2lib/features/battle_league/presentation/ui/first_part/widgets/topic_page/topic_widget.dart
-
59lib/features/battle_league/presentation/ui/second_part/battle_league_result_page.dart
-
89lib/features/battle_league/presentation/ui/second_part/bl_question_page.dart
-
6lib/features/battle_league/presentation/ui/second_part/widgets/battle_league_question_avatar.dart
-
26lib/features/battle_league/presentation/ui/second_part/widgets/question_board.dart
-
0lib/features/battle_league/presentation/ui/second_part/widgets/question_timer_ready_widget.dart
-
28lib/features/battle_league/question_part/bl_question/data/datasource/bl_question_datasource.dart
-
13lib/features/battle_league/question_part/bl_question/data/model/bl_question_model.dart
-
29lib/features/battle_league/question_part/bl_question/data/repository_impl/bl_question_repository_impl.dart
-
14lib/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart
-
8lib/features/battle_league/question_part/bl_question/domain/repository/bl_question_repository.dart
-
19lib/features/battle_league/question_part/bl_question/domain/usecases/get_bl_question_usecase.dart
-
20lib/features/battle_league/question_part/bl_question/presentation/binding/bl_question_binding.dart
-
54lib/features/battle_league/question_part/bl_question/presentation/controller/bl_question_controller.dart
-
34lib/features/battle_league/question_part/bl_question/presentation/ui/bl_question_page.dart
-
23lib/init_bindings.dart
-
2pubspec.yaml
@ -1,5 +0,0 @@ |
|||
class BattleLeagueParams { |
|||
int? id; |
|||
|
|||
BattleLeagueParams({this.id}); |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
class BlParams { |
|||
List<int>? chooseTopics; |
|||
|
|||
BlParams({this.chooseTopics}); |
|||
|
|||
Map<String, dynamic> get toCockJson => { |
|||
'client_time': DateTime.now().millisecondsSinceEpoch, |
|||
}; |
|||
|
|||
Map<String, dynamic> get toMatchMakingJson => { |
|||
'skill_level': 0, |
|||
if (chooseTopics?.isNotEmpty ?? false) 'topic_ids': chooseTopics, |
|||
}; |
|||
} |
|||
@ -0,0 +1,176 @@ |
|||
import 'dart:convert'; |
|||
|
|||
import 'package:centrifuge/centrifuge.dart'; |
|||
import 'package:flutter/foundation.dart'; |
|||
|
|||
class WebSocketService { |
|||
final Client _client; |
|||
late final Subscription _subscription; |
|||
|
|||
WebSocketService(this._client) { |
|||
_attachListeners(); |
|||
} |
|||
|
|||
/// ------- Connection ------- |
|||
Future<void> connect() async { |
|||
try { |
|||
await _client.connect(); |
|||
} catch (e) { |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
void _attachListeners() { |
|||
_client.connecting.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Connection: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.connected.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Connected: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.disconnected.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Disconnected: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.message.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Message: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.error.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Error: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.join.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Join: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.leave.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Leave: $event'); |
|||
} |
|||
}); |
|||
|
|||
_client.publication.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Client Publication: $event'); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
Stream<ConnectingEvent> get clientConnecting => _client.connecting; |
|||
|
|||
Stream<ConnectedEvent> get clientConnected => _client.connected; |
|||
|
|||
Stream<DisconnectedEvent> get clientDisconnected => _client.disconnected; |
|||
|
|||
Stream<ServerPublicationEvent> get clientPublication => _client.publication; |
|||
|
|||
Future<void> disconnect() async { |
|||
try { |
|||
await _client.disconnect(); |
|||
} catch (e) { |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
Future<RPCResult> sendRpc({ |
|||
required String method, |
|||
required Map<String, dynamic> data, |
|||
}) async { |
|||
if (_client.state != State.connected) { |
|||
throw Exception('Websocket Is NOT Connected'); |
|||
} |
|||
try { |
|||
final RPCResult result = await _client.rpc(method, utf8.encode(json.encode(data))); |
|||
if (kDebugMode) { |
|||
print('RPC sent successfully. Result: $result'); |
|||
} |
|||
return result; |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
print('Error sending RPC: $e'); |
|||
} |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// ------- Connection ------- |
|||
Future<void> subscribe({required String channel}) async { |
|||
try { |
|||
_subscription = _client.newSubscription(channel); |
|||
_attachSubscribeListeners(); |
|||
await _subscription.subscribe(); |
|||
} catch (e) { |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
void _attachSubscribeListeners() { |
|||
_subscription.error.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Error: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.join.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Join: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.leave.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Leave: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.publication.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Publication: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.subscribing.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Subscribing: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.subscribed.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Subscribed: $event'); |
|||
} |
|||
}); |
|||
|
|||
_subscription.unsubscribed.listen((event) { |
|||
if (kDebugMode) { |
|||
print('Subscribe Unsubscribed: $event'); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
Stream<JoinEvent> get subscribeJoin => _subscription.join; |
|||
|
|||
Stream<PublicationEvent> get subscribePublication => _subscription.publication; |
|||
|
|||
Future<void> unSubscribe() async { |
|||
try { |
|||
await _subscription.unsubscribe(); |
|||
} catch (e) { |
|||
rethrow; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
import 'package:shia_game_flutter/core/constants/my_api.dart'; |
|||
import 'package:shia_game_flutter/core/network/http_request.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/response/base_response.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/clock_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/match_making_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/player_token_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/topics_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/clock_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/match_making_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
|
|||
abstract class IBattleLeagueDatasource { |
|||
Future<PlayerTokenEntity> getPlayerToken({required BlParams params}); |
|||
Future<List<TopicsEntity>> getTopics({required BlParams params}); |
|||
Future<ClockEntity> getClock({required BlParams params}); |
|||
Future<MatchMakingEntity> matchMaking({required BlParams params}); |
|||
} |
|||
|
|||
class BattleLeagueDatasourceImpl implements IBattleLeagueDatasource { |
|||
final IHttpRequest httpRequest; |
|||
|
|||
const BattleLeagueDatasourceImpl(this.httpRequest); |
|||
|
|||
@override |
|||
Future<PlayerTokenEntity> getPlayerToken({required BlParams params}) async { |
|||
final response = await httpRequest.post(path: MyApi.centrifugoToken); |
|||
|
|||
return BaseResponse.getData<PlayerTokenEntity>( |
|||
response, |
|||
(json) => PlayerTokenModel.fromJson(json), |
|||
); |
|||
} |
|||
|
|||
@override |
|||
Future<List<TopicsEntity>> getTopics({required BlParams params}) async { |
|||
final response = await httpRequest.get(path: MyApi.topics); |
|||
|
|||
return BaseResponse.getDataList<TopicsEntity>( |
|||
response?['results'], |
|||
(json) => TopicsModel.fromJson(json), |
|||
); |
|||
} |
|||
|
|||
@override |
|||
Future<ClockEntity> getClock({required BlParams params}) async { |
|||
final response = await httpRequest.post( |
|||
path: MyApi.clock, |
|||
data: params.toCockJson, |
|||
); |
|||
|
|||
return BaseResponse.getData<ClockEntity>( |
|||
response, |
|||
(json) => ClockModel.fromJson(json), |
|||
); |
|||
} |
|||
|
|||
@override |
|||
Future<MatchMakingEntity> matchMaking({required BlParams params}) async { |
|||
final response = await httpRequest.post( |
|||
path: MyApi.matchMaking, |
|||
data: params.toMatchMakingJson, |
|||
); |
|||
|
|||
return BaseResponse.getData<MatchMakingEntity>( |
|||
response, |
|||
(json) => MatchMakingModel.fromJson(json), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/answer_entity.dart'; |
|||
|
|||
class AnswerModel extends AnswerEntity { |
|||
const AnswerModel({super.id, super.text}); |
|||
|
|||
factory AnswerModel.fromJson(Map<String, dynamic> json) { |
|||
return AnswerModel(id: json['id'], text: json['text']); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/clock_entity.dart'; |
|||
|
|||
class ClockModel extends ClockEntity { |
|||
const ClockModel({ |
|||
super.serverTime, |
|||
super.clientTime, |
|||
super.serverReceiveTime, |
|||
}); |
|||
|
|||
factory ClockModel.fromJson(Map<String, dynamic> json) { |
|||
return ClockModel( |
|||
clientTime: json['client_time'], |
|||
serverTime: json['server_time'], |
|||
serverReceiveTime: json['server_receive_time'], |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/match_making_entity.dart'; |
|||
|
|||
class MatchMakingModel extends MatchMakingEntity { |
|||
const MatchMakingModel({ |
|||
super.estimatedWaitTime, |
|||
super.message, |
|||
super.position, |
|||
super.queueSize, |
|||
super.selectedTopics, |
|||
super.status, |
|||
}); |
|||
|
|||
factory MatchMakingModel.fromJson(Map<String, dynamic> json) { |
|||
return MatchMakingModel( |
|||
estimatedWaitTime: json['estimated_wait_time'], |
|||
message: json['message'], |
|||
position: json['position'], |
|||
queueSize: json['queue_size'], |
|||
selectedTopics: json['selected_topics']?.map<int>((e) => int.parse('$e')).toList(), |
|||
status: json['status'], |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
|
|||
class PlayerTokenModel extends PlayerTokenEntity { |
|||
const PlayerTokenModel({super.token, super.user, super.channel}); |
|||
|
|||
factory PlayerTokenModel.fromJson(Map<String, dynamic> json) { |
|||
return PlayerTokenModel( |
|||
token: json['token'], |
|||
user: json['user'], |
|||
channel: json['channel'], |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/answer_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/answer_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/question_entity.dart'; |
|||
|
|||
class QuestionModel extends QuestionEntity { |
|||
const QuestionModel({super.id, super.text, super.options, super.timeLimit}); |
|||
|
|||
factory QuestionModel.fromJson(Map<String, dynamic> json) { |
|||
return QuestionModel( |
|||
id: json['id'], |
|||
text: json['text'], |
|||
options: json['options'] |
|||
?.map<AnswerEntity>((e) => AnswerModel.fromJson(e)) |
|||
.toList(), |
|||
timeLimit: json['time_limit'], |
|||
); |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
|
|||
class TopicsModel extends TopicsEntity { |
|||
const TopicsModel({ |
|||
@ -0,0 +1,94 @@ |
|||
import 'package:flutter/foundation.dart'; |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/datasource/battle_league_datasource.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/clock_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/match_making_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class BattleLeagueRepositoryImpl implements IBattleLeagueRepository { |
|||
final IBattleLeagueDatasource datasource; |
|||
|
|||
const BattleLeagueRepositoryImpl(this.datasource); |
|||
|
|||
@override |
|||
Future<DataState<PlayerTokenEntity, MyException>> getPlayerToken({ |
|||
required BlParams params, |
|||
}) async { |
|||
try { |
|||
final PlayerTokenEntity response = await datasource.getPlayerToken( |
|||
params: params, |
|||
); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@override |
|||
Future<DataState<List<TopicsEntity>, MyException>> getTopics({ |
|||
required BlParams params, |
|||
}) async { |
|||
try { |
|||
final List<TopicsEntity> response = await datasource.getTopics( |
|||
params: params, |
|||
); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@override |
|||
Future<DataState<ClockEntity, MyException>> getClock({ |
|||
required BlParams params, |
|||
}) async { |
|||
try { |
|||
final ClockEntity response = await datasource.getClock(params: params); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@override |
|||
Future<DataState<MatchMakingEntity, MyException>> matchMaking({ |
|||
required BlParams params, |
|||
}) async { |
|||
try { |
|||
final MatchMakingEntity response = await datasource.matchMaking( |
|||
params: params, |
|||
); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class AnswerEntity extends Equatable { |
|||
final int? id; |
|||
final String? text; |
|||
|
|||
const AnswerEntity({this.id, this.text}); |
|||
|
|||
@override |
|||
List<Object?> get props => [id, text]; |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class ClockEntity extends Equatable { |
|||
final double? serverTime; |
|||
final double? clientTime; |
|||
final double? serverReceiveTime; |
|||
|
|||
const ClockEntity({this.serverTime, this.clientTime, this.serverReceiveTime}); |
|||
|
|||
@override |
|||
List<Object?> get props => [serverTime, clientTime, serverReceiveTime]; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class MatchMakingEntity extends Equatable { |
|||
final int? estimatedWaitTime; |
|||
final String? message; |
|||
final int? position; |
|||
final int? queueSize; |
|||
final List<int>? selectedTopics; |
|||
final String? status; |
|||
|
|||
const MatchMakingEntity({ |
|||
this.estimatedWaitTime, |
|||
this.message, |
|||
this.position, |
|||
this.queueSize, |
|||
this.selectedTopics, |
|||
this.status, |
|||
}); |
|||
|
|||
@override |
|||
List<Object?> get props => [ |
|||
estimatedWaitTime, |
|||
message, |
|||
position, |
|||
queueSize, |
|||
selectedTopics, |
|||
status, |
|||
]; |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class PlayerTokenEntity extends Equatable { |
|||
final String? token; |
|||
final String? user; |
|||
final String? channel; |
|||
|
|||
const PlayerTokenEntity({this.token, this.user, this.channel}); |
|||
|
|||
@override |
|||
List<Object?> get props => [token, user, channel]; |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/answer_entity.dart'; |
|||
|
|||
class QuestionEntity extends Equatable { |
|||
final int? id; |
|||
final String? text; |
|||
final List<AnswerEntity>? options; |
|||
final int? timeLimit; |
|||
|
|||
const QuestionEntity({this.id, this.text, this.options, this.timeLimit}); |
|||
|
|||
@override |
|||
List<Object?> get props => [id, text, options, timeLimit]; |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/clock_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/match_making_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
|
|||
abstract class IBattleLeagueRepository { |
|||
Future<DataState<PlayerTokenEntity, MyException>> getPlayerToken({ |
|||
required BlParams params, |
|||
}); |
|||
Future<DataState<List<TopicsEntity>, MyException>> getTopics({ |
|||
required BlParams params, |
|||
}); |
|||
Future<DataState<ClockEntity, MyException>> getClock({ |
|||
required BlParams params, |
|||
}); |
|||
Future<DataState<MatchMakingEntity, MyException>> matchMaking({ |
|||
required BlParams params, |
|||
}); |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/clock_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class GetClockUseCase implements UseCase<ClockEntity, BlParams> { |
|||
final IBattleLeagueRepository repository; |
|||
|
|||
const GetClockUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<ClockEntity, MyException>> call(BlParams params) { |
|||
return repository.getClock(params: params); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class GetPlayerTokenUseCase |
|||
implements UseCase<PlayerTokenEntity, BlParams> { |
|||
final IBattleLeagueRepository repository; |
|||
|
|||
const GetPlayerTokenUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<PlayerTokenEntity, MyException>> call(BlParams params) { |
|||
return repository.getPlayerToken(params: params); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class GetTopicsUseCase |
|||
implements UseCase<List<TopicsEntity>, BlParams> { |
|||
final IBattleLeagueRepository repository; |
|||
|
|||
const GetTopicsUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<List<TopicsEntity>, MyException>> call(BlParams params,) { |
|||
return repository.getTopics(params: params); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/match_making_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class MatchMakingUseCase implements UseCase<MatchMakingEntity, BlParams> { |
|||
final IBattleLeagueRepository repository; |
|||
|
|||
const MatchMakingUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<MatchMakingEntity, MyException>> call(BlParams params) { |
|||
return repository.matchMaking(params: params); |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/constants/my_api.dart'; |
|||
import 'package:shia_game_flutter/core/network/http_request.dart'; |
|||
import 'package:shia_game_flutter/core/params/battle_league_params.dart'; |
|||
import 'package:shia_game_flutter/core/response/base_response.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/data/model/topics_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
|
|||
abstract class IBattleLeagueDatasource { |
|||
Future<List<TopicsEntity>> getTopics({required BattleLeagueParams params}); |
|||
} |
|||
|
|||
class BattleLeagueDatasourceImpl implements IBattleLeagueDatasource { |
|||
final IHttpRequest httpRequest; |
|||
|
|||
const BattleLeagueDatasourceImpl(this.httpRequest); |
|||
|
|||
@override |
|||
Future<List<TopicsEntity>> getTopics({ |
|||
required BattleLeagueParams params, |
|||
}) async { |
|||
final response = await httpRequest.get(path: MyApi.topics); |
|||
|
|||
return BaseResponse.getDataList<TopicsEntity>( |
|||
response?['results'], |
|||
(json) => TopicsModel.fromJson(json), |
|||
); |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/battle_league_entity.dart'; |
|||
|
|||
class BattleLeagueModel extends BattleLeagueEntity { |
|||
const BattleLeagueModel({ |
|||
super.id, |
|||
}); |
|||
|
|||
factory BattleLeagueModel.fromJson(Map<String, dynamic> json) { |
|||
return BattleLeagueModel( |
|||
id: json['id'], |
|||
); |
|||
} |
|||
} |
|||
|
|||
@ -1,33 +0,0 @@ |
|||
import 'package:flutter/foundation.dart'; |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/battle_league_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/data/datasource/battle_league_datasource.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class BattleLeagueRepositoryImpl implements IBattleLeagueRepository { |
|||
final IBattleLeagueDatasource datasource; |
|||
|
|||
const BattleLeagueRepositoryImpl(this.datasource); |
|||
|
|||
@override |
|||
Future<DataState<List<TopicsEntity>, MyException>> getTopics({ |
|||
required BattleLeagueParams params, |
|||
}) async { |
|||
try { |
|||
final List<TopicsEntity> response = await datasource.getTopics( |
|||
params: params, |
|||
); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class BattleLeagueEntity extends Equatable { |
|||
final int? id; |
|||
|
|||
const BattleLeagueEntity({ |
|||
this.id, |
|||
}); |
|||
|
|||
@override |
|||
List<Object?> get props => [ |
|||
id, |
|||
]; |
|||
} |
|||
|
|||
@ -1,10 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/battle_league_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
|
|||
abstract class IBattleLeagueRepository { |
|||
Future<DataState<List<TopicsEntity>, MyException>> getTopics({ |
|||
required BattleLeagueParams params, |
|||
}); |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/battle_league_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/repository/battle_league_repository.dart'; |
|||
|
|||
class GetTopicsUseCase |
|||
implements UseCase<List<TopicsEntity>, BattleLeagueParams> { |
|||
final IBattleLeagueRepository repository; |
|||
|
|||
const GetTopicsUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<List<TopicsEntity>, MyException>> call(BattleLeagueParams params,) { |
|||
return repository.getTopics(params: params); |
|||
} |
|||
} |
|||
@ -1,87 +0,0 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:shia_game_flutter/core/params/battle_league_params.dart'; |
|||
import 'package:shia_game_flutter/core/routers/my_routes.dart'; |
|||
import 'package:shia_game_flutter/core/status/base_status.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/domain/usecases/get_topics_usecase.dart'; |
|||
|
|||
class BattleLeagueController extends GetxController |
|||
with StateMixin, GetSingleTickerProviderStateMixin { |
|||
/// ----- Constructor ----- |
|||
BattleLeagueController(this._getTopicsUseCase); |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
tabController = TabController(length: 2, vsync: this); |
|||
} |
|||
|
|||
@override |
|||
void onClose() { |
|||
textEditingController.dispose(); |
|||
super.onClose(); |
|||
} |
|||
|
|||
/// ----- UseCases ----- |
|||
final GetTopicsUseCase _getTopicsUseCase; |
|||
|
|||
/// ----- Variables ----- |
|||
final BattleLeagueParams battleLeagueParams = BattleLeagueParams(); |
|||
final RxList<TopicsEntity> topicList = RxList.empty(); |
|||
final RxList<TopicsEntity> chooseTopicList = RxList.empty(); |
|||
|
|||
/// ------ Controllers ------ |
|||
final TextEditingController textEditingController = TextEditingController(); |
|||
late final TabController tabController; |
|||
|
|||
/// ------ Statuses ------ |
|||
final Rx<BaseStatus> getBattleLeagueStatus = Rx(const BaseInit()); |
|||
final Rx<BaseStatus> getTopicsStatus = Rx(const BaseInit()); |
|||
|
|||
/// ------ Functions ------ |
|||
void goToTopicPage() { |
|||
getTopics(); |
|||
Get.toNamed(Routes.battleLeagueTopicPage); |
|||
} |
|||
|
|||
void goToFindingPage() { |
|||
Get.toNamed(Routes.battleLeagueFindingPage); |
|||
} |
|||
|
|||
void goToFoundedPage() { |
|||
Get.toNamed(Routes.battleLeagueFoundedPage); |
|||
} |
|||
|
|||
void chooseTopic(TopicsEntity topic) { |
|||
if (chooseTopicList.contains(topic)) { |
|||
chooseTopicList.remove(topic); |
|||
} else { |
|||
if (chooseTopicList.length < 3) { |
|||
chooseTopicList.add(topic); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void choosRandomTopic() { |
|||
final List<TopicsEntity> newTopicList = List.from(topicList); |
|||
newTopicList.shuffle(); |
|||
chooseTopicList.value = newTopicList.take(3).toList(); |
|||
} |
|||
|
|||
/// ------ Api Calls ------ |
|||
Future<void> getTopics() async { |
|||
getTopicsStatus.value = const BaseLoading(); |
|||
await _getTopicsUseCase(battleLeagueParams).then( |
|||
(value) => value.fold( |
|||
(data) { |
|||
topicList.value = data; |
|||
getTopicsStatus.value = const BaseComplete(); |
|||
}, |
|||
(error) { |
|||
getTopicsStatus.value = BaseError(errorMessage: error.errorMessage); |
|||
}, |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -1,10 +1,15 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/first_part/presentation/controller/battle_league_controller.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/controller/battle_league_controller.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class BattleLeagueBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.put<BattleLeagueController>(BattleLeagueController(Get.find())); |
|||
Get.put<BattleLeagueController>(BattleLeagueController( |
|||
Get.find(), |
|||
Get.find(), |
|||
Get.find(), |
|||
Get.find(), |
|||
)); |
|||
} |
|||
|
|||
Future<void> deleteBindings() async { |
|||
@ -0,0 +1,280 @@ |
|||
import 'dart:async'; |
|||
import 'dart:convert'; |
|||
|
|||
import 'package:centrifuge/centrifuge.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:shia_game_flutter/core/auth_storage/auth_storage.dart'; |
|||
import 'package:shia_game_flutter/core/constants/my_api.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_params.dart'; |
|||
import 'package:shia_game_flutter/core/routers/my_routes.dart'; |
|||
import 'package:shia_game_flutter/core/services/web_socket_service.dart'; |
|||
import 'package:shia_game_flutter/core/status/base_status.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/data/model/question_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/player_token_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/question_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/entity/topics_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/usecases/get_clock_usecase.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/usecases/get_player_token_usecase.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/usecases/get_topics_usecase.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/domain/usecases/match_making_usecase.dart'; |
|||
|
|||
class BattleLeagueController extends GetxController |
|||
with StateMixin, GetSingleTickerProviderStateMixin { |
|||
/// ----- Constructor ----- |
|||
BattleLeagueController( |
|||
this._getTopicsUseCase, |
|||
this._getPlayerTokenUseCase, |
|||
this._getClockUseCase, |
|||
this._matchMakingUseCase, |
|||
); |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
tabController = TabController(length: 2, vsync: this); |
|||
// getPlayerToken(); |
|||
} |
|||
|
|||
@override |
|||
void onClose() { |
|||
textEditingController.dispose(); |
|||
_webSocketService.disconnect(); |
|||
timer?.cancel(); |
|||
timer = null; |
|||
super.onClose(); |
|||
} |
|||
|
|||
/// ----- UseCases ----- |
|||
final GetTopicsUseCase _getTopicsUseCase; |
|||
final GetPlayerTokenUseCase _getPlayerTokenUseCase; |
|||
final GetClockUseCase _getClockUseCase; |
|||
final MatchMakingUseCase _matchMakingUseCase; |
|||
final WebSocketService _webSocketService = WebSocketService( |
|||
createClient( |
|||
MyApi.webSocketUrl, |
|||
ClientConfig( |
|||
name: 'Amirreza', |
|||
data: utf8.encode(jsonEncode({'player_token': AuthStorage.token})), |
|||
), |
|||
), |
|||
); |
|||
|
|||
/// ----- Variables ----- |
|||
final BlParams blParams = BlParams(); |
|||
final RxList<TopicsEntity> topicList = RxList.empty(); |
|||
final RxList<TopicsEntity> chooseTopicList = RxList.empty(); |
|||
PlayerTokenEntity playerToken = const PlayerTokenEntity(); |
|||
final Rx<QuestionEntity> question = Rx(const QuestionEntity()); |
|||
int battleId = 0; |
|||
final RxInt roundNumber = RxInt(0); |
|||
final Rx<bool?> isCorrect = Rx(null); |
|||
final RxInt selectedIndex = RxInt(-1); |
|||
final RxInt roundTimer = RxInt(0); |
|||
final RxInt myWonNumber = RxInt(0); |
|||
final RxInt enemyWonNumber = RxInt(0); |
|||
Timer? timer; |
|||
String clientID = ''; |
|||
String player1ID = ''; |
|||
String player1Username = ''; |
|||
String player2ID = ''; |
|||
String player2Username = ''; |
|||
String myId = ''; |
|||
String enemyId = ''; |
|||
final RxString enemyUsername = RxString(''); |
|||
final Rx<String?> winnerId = Rx(null); |
|||
|
|||
/// ------ Controllers ------ |
|||
final TextEditingController textEditingController = TextEditingController(); |
|||
late final TabController tabController; |
|||
|
|||
/// ------ Statuses ------ |
|||
final Rx<BaseStatus> getTopicsStatus = Rx(const BaseInit()); |
|||
final Rx<BaseStatus> connectStatus = Rx(const BaseLoading()); |
|||
final Rx<BaseStatus> getClockStatus = Rx(const BaseInit()); |
|||
final Rx<BaseStatus> matchMakingStatus = Rx(const BaseInit()); |
|||
|
|||
/// ------ Functions ------ |
|||
void goToTopicPage() { |
|||
getTopics(); |
|||
Get.toNamed(Routes.battleLeagueTopicPage); |
|||
} |
|||
|
|||
void goToFindingPage() { |
|||
print(AuthStorage.token); |
|||
Get.toNamed(Routes.battleLeagueFindingPage); |
|||
_webSocketService.connect(); |
|||
_webSocketService.clientConnected.listen((event) { |
|||
clientID = json.decode(utf8.decode(event.data))['player_id']; |
|||
print(clientID); |
|||
getClock(); |
|||
connectStatus.value = const BaseComplete(); |
|||
}); |
|||
_webSocketService.clientPublication.listen((event) async { |
|||
final String type = json.decode(utf8.decode(event.data))['type']; |
|||
|
|||
if (type == 'match_found'){ |
|||
final String channel = json.decode( |
|||
utf8.decode(event.data), |
|||
)['battle_channel']; |
|||
final String battleId = json.decode(utf8.decode(event.data))['battle_id']; |
|||
player1ID = json.decode(utf8.decode(event.data))['player1']['id']; |
|||
player1Username = json.decode(utf8.decode(event.data))['player1']['username']; |
|||
player2ID = json.decode(utf8.decode(event.data))['player2']['id']; |
|||
player2Username = json.decode(utf8.decode(event.data))['player2']['username']; |
|||
if (player1ID == clientID) { |
|||
myId = player1ID; |
|||
enemyId = player2ID; |
|||
enemyUsername.value = player2Username; |
|||
} else { |
|||
myId = player2ID; |
|||
enemyId = player1ID; |
|||
enemyUsername.value = player1Username; |
|||
} |
|||
this.battleId = int.parse(battleId); |
|||
|
|||
await _webSocketService.subscribe(channel: channel); |
|||
_webSocketService.subscribeJoin.listen((event) { |
|||
Get.offNamed(Routes.battleLeagueQuestionPage); |
|||
}); |
|||
|
|||
_webSocketService.subscribePublication.listen((event) { |
|||
selectedIndex.value = -1; |
|||
isCorrect.value = null; |
|||
final String type = json.decode(utf8.decode(event.data))['type']; |
|||
|
|||
if (type == 'round_start') { |
|||
question.value = QuestionModel.fromJson( |
|||
json.decode(utf8.decode(event.data))['question'], |
|||
); |
|||
roundNumber.value = json.decode(utf8.decode(event.data))['round_number']; |
|||
roundTimer.value = json.decode(utf8.decode(event.data))['duration_seconds']; |
|||
timer?.cancel(); |
|||
timer = null; |
|||
timer = Timer.periodic(const Duration(seconds: 1), (timer) { |
|||
roundTimer.value--; |
|||
}); |
|||
} |
|||
|
|||
if (type == 'battle_complete'){ |
|||
timer?.cancel(); |
|||
timer = null; |
|||
winnerId.value = json.decode(utf8.decode(event.data))['winner_id']; |
|||
Get.offNamed(Routes.battleLeagueResultPage); |
|||
} |
|||
|
|||
if (type == 'question_locked') { |
|||
final String lockedBy = json.decode( |
|||
utf8.decode(event.data))['locked_by']; |
|||
if (lockedBy == enemyId) { |
|||
print('rich enemy'); |
|||
enemyWonNumber.value = enemyWonNumber.value + 1; |
|||
} else { |
|||
print('rich myself'); |
|||
myWonNumber.value = myWonNumber.value + 1; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
Future<void> cancelFinding() async { |
|||
Get.back(); |
|||
connectStatus.value = const BaseLoading(); |
|||
await _webSocketService.unSubscribe(); |
|||
_webSocketService.disconnect(); |
|||
} |
|||
|
|||
void chooseTopic(TopicsEntity topic) { |
|||
if (chooseTopicList.contains(topic)) { |
|||
chooseTopicList.remove(topic); |
|||
} else { |
|||
if (chooseTopicList.length < 3) { |
|||
chooseTopicList.add(topic); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void choosRandomTopic() { |
|||
final List<TopicsEntity> newTopicList = List.from(topicList); |
|||
newTopicList.shuffle(); |
|||
chooseTopicList.value = newTopicList.take(3).toList(); |
|||
} |
|||
|
|||
Future<void> backToTopicPage() async { |
|||
Get.back(); |
|||
await _webSocketService.unSubscribe(); |
|||
_webSocketService.disconnect(); |
|||
} |
|||
|
|||
void chooseAnswer({required int index}) async { |
|||
selectedIndex.value = index; |
|||
await _webSocketService.sendRpc( |
|||
method: 'battle:submit_answer', |
|||
data: { |
|||
'battle_id': battleId, |
|||
'round_number': roundNumber.value, |
|||
'selected_option': index, |
|||
'client_timestamp': DateTime.timestamp().millisecondsSinceEpoch / 1000, |
|||
}, |
|||
).then((value) { |
|||
isCorrect.value = json.decode(utf8.decode(value.data))['is_correct']; |
|||
},); |
|||
} |
|||
|
|||
/// ------ Api Calls ------ |
|||
Future<void> getPlayerToken() async { |
|||
await _getPlayerTokenUseCase(blParams).then( |
|||
(value) => value.fold((data) { |
|||
playerToken = data; |
|||
_webSocketService.connect(); |
|||
}, (error) {}), |
|||
); |
|||
} |
|||
|
|||
Future<void> getTopics() async { |
|||
getTopicsStatus.value = const BaseLoading(); |
|||
await _getTopicsUseCase(blParams).then( |
|||
(value) => value.fold( |
|||
(data) { |
|||
topicList.value = data; |
|||
getTopicsStatus.value = const BaseComplete(); |
|||
}, |
|||
(error) { |
|||
getTopicsStatus.value = BaseError(errorMessage: error.errorMessage); |
|||
}, |
|||
), |
|||
); |
|||
} |
|||
|
|||
Future<void> getClock() async { |
|||
getClockStatus.value = const BaseLoading(); |
|||
await _getClockUseCase(blParams).then( |
|||
(value) => value.fold( |
|||
(data) { |
|||
matchMaking(); |
|||
getClockStatus.value = const BaseComplete(); |
|||
}, |
|||
(error) { |
|||
getClockStatus.value = BaseError(errorMessage: error.errorMessage); |
|||
}, |
|||
), |
|||
); |
|||
} |
|||
|
|||
Future<void> matchMaking() async { |
|||
matchMakingStatus.value = const BaseLoading(); |
|||
blParams.chooseTopics = chooseTopicList.map((e) => e.id ?? 0).toList(); |
|||
await _matchMakingUseCase(blParams).then( |
|||
(value) => value.fold( |
|||
(data) { |
|||
matchMakingStatus.value = const BaseComplete(); |
|||
}, |
|||
(error) { |
|||
matchMakingStatus.value = BaseError(errorMessage: error.errorMessage); |
|||
}, |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:shia_game_flutter/common_ui/resources/my_text_style.dart'; |
|||
import 'package:shia_game_flutter/core/utils/gap.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/controller/battle_league_controller.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/ui/first_part/widgets/button/battle_purple_button.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/ui/second_part/widgets/question_board.dart'; |
|||
|
|||
class BattleLeagueResultPage extends GetView<BattleLeagueController> { |
|||
const BattleLeagueResultPage({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Scaffold( |
|||
body: DecoratedBox( |
|||
decoration: const BoxDecoration( |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [Color(0XFF390F72), Color(0XFF160C30)], |
|||
), |
|||
), |
|||
child: SafeArea( |
|||
child: Column( |
|||
children: [ |
|||
Obx( |
|||
() => QuestionBoard( |
|||
enemyWonNumber: controller.enemyWonNumber.value, |
|||
myWonNumber: controller.myWonNumber.value, |
|||
roundNumber: controller.roundNumber.value, |
|||
enemyUserName: controller.enemyUsername.value, |
|||
), |
|||
), |
|||
50.0.gapHeight, |
|||
Obx( |
|||
() => Text( |
|||
controller.winnerId.value == controller.myId |
|||
? 'YOU WON GAME' |
|||
: controller.winnerId.value == controller.enemyId |
|||
? 'YOU LOOS GAME' |
|||
: 'DRAW GAME', |
|||
style: Lexend.black, |
|||
), |
|||
), |
|||
Spacer(), |
|||
Padding( |
|||
padding: const EdgeInsets.symmetric(horizontal: 16), |
|||
child: BattlePurpleButton( |
|||
label: 'Play Another Game', |
|||
onTap: controller.backToTopicPage, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:shia_game_flutter/common_ui/resources/my_colors.dart'; |
|||
import 'package:shia_game_flutter/common_ui/resources/my_text_style.dart'; |
|||
import 'package:shia_game_flutter/core/utils/gap.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/controller/battle_league_controller.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/presentation/ui/second_part/widgets/question_board.dart'; |
|||
|
|||
class BLQuestionPage extends GetView<BattleLeagueController> { |
|||
const BLQuestionPage({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Scaffold( |
|||
body: DecoratedBox( |
|||
decoration: const BoxDecoration( |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [Color(0XFF390F72), Color(0XFF160C30)], |
|||
), |
|||
), |
|||
child: SafeArea( |
|||
child: Column( |
|||
children: [ |
|||
Obx( |
|||
() => QuestionBoard( |
|||
enemyWonNumber: controller.enemyWonNumber.value, |
|||
myWonNumber: controller.myWonNumber.value, |
|||
roundNumber: controller.roundNumber.value, |
|||
enemyUserName: controller.enemyUsername.value, |
|||
), |
|||
), |
|||
50.0.gapHeight, |
|||
Obx( |
|||
() => Text( |
|||
'${controller.roundTimer.value}', |
|||
style: Lexend.black, |
|||
), |
|||
), |
|||
25.0.gapHeight, |
|||
Obx( |
|||
() => Text( |
|||
controller.question.value.text ?? '', |
|||
style: Lexend.bold, |
|||
), |
|||
), |
|||
const Spacer(), |
|||
Obx( |
|||
() => ListView.separated( |
|||
itemCount: controller.question.value.options?.length ?? 0, |
|||
shrinkWrap: true, |
|||
padding: const EdgeInsets.symmetric(horizontal: 20), |
|||
itemBuilder: (context, index) => Material( |
|||
type: MaterialType.transparency, |
|||
child: Obx( |
|||
() => ListTile( |
|||
selected: index == controller.selectedIndex.value, |
|||
selectedColor: MyColors.white, |
|||
selectedTileColor: controller.isCorrect.value == true |
|||
? Colors.green |
|||
: controller.isCorrect.value == false |
|||
? Colors.red |
|||
: Colors.yellow, |
|||
onTap: () { |
|||
controller.chooseAnswer(index: index); |
|||
}, |
|||
title: Text( |
|||
controller.question.value.options?[index].text ?? '', |
|||
textDirection: TextDirection.rtl, |
|||
), |
|||
titleTextStyle: Lexend.bold, |
|||
shape: const StadiumBorder( |
|||
side: BorderSide(width: 1, color: MyColors.white), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
separatorBuilder: (context, index) => 12.0.gapHeight, |
|||
), |
|||
), |
|||
const Spacer(), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/constants/my_api.dart'; |
|||
import 'package:shia_game_flutter/core/network/http_request.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_question_params.dart'; |
|||
import 'package:shia_game_flutter/core/response/base_response.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/data/model/bl_question_model.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
|
|||
abstract class IBLQuestionDatasource { |
|||
Future<BLQuestionEntity> getData({required BLQuestionParams params}); |
|||
} |
|||
|
|||
class BLQuestionDatasourceImpl implements IBLQuestionDatasource { |
|||
final IHttpRequest httpRequest; |
|||
|
|||
const BLQuestionDatasourceImpl(this.httpRequest); |
|||
|
|||
@override |
|||
Future<BLQuestionEntity> getData({required BLQuestionParams params}) async { |
|||
final response = await httpRequest.get( |
|||
path: MyApi.baseUrl, |
|||
); |
|||
|
|||
return BaseResponse.getData<BLQuestionEntity>( |
|||
response?['data'], |
|||
(json) => BLQuestionModel.fromJson(json), |
|||
); |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
|
|||
class BLQuestionModel extends BLQuestionEntity { |
|||
const BLQuestionModel({ |
|||
super.id, |
|||
}); |
|||
|
|||
factory BLQuestionModel.fromJson(Map<String, dynamic> json) { |
|||
return BLQuestionModel( |
|||
id: json['id'], |
|||
); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
import 'package:flutter/foundation.dart'; |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_question_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/data/datasource/bl_question_datasource.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/repository/bl_question_repository.dart'; |
|||
|
|||
class BLQuestionRepositoryImpl implements IBLQuestionRepository { |
|||
final IBLQuestionDatasource datasource; |
|||
|
|||
const BLQuestionRepositoryImpl(this.datasource); |
|||
|
|||
@override |
|||
Future<DataState<BLQuestionEntity, MyException>> getData({required BLQuestionParams params}) async { |
|||
try { |
|||
final BLQuestionEntity response = await datasource.getData(params: params); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class BLQuestionEntity extends Equatable { |
|||
final int? id; |
|||
|
|||
const BLQuestionEntity({ |
|||
this.id, |
|||
}); |
|||
|
|||
@override |
|||
List<Object?> get props => [ |
|||
id, |
|||
]; |
|||
} |
|||
@ -1,8 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_question_params.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
|
|||
abstract class IBLQuestionRepository { |
|||
Future<DataState<BLQuestionEntity, MyException>> getData({required BLQuestionParams params}); |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
import 'package:shia_game_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_question_params.dart'; |
|||
import 'package:shia_game_flutter/core/usecase/usecase.dart'; |
|||
import 'package:shia_game_flutter/core/utils/data_state.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/repository/bl_question_repository.dart'; |
|||
|
|||
class GetBLQuestionUseCase implements UseCase<BLQuestionEntity, BLQuestionParams> { |
|||
final IBLQuestionRepository repository; |
|||
|
|||
const GetBLQuestionUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<BLQuestionEntity, MyException>> call(BLQuestionParams params) { |
|||
return repository.getData(params: params); |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -1,20 +0,0 @@ |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/presentation/controller/bl_question_controller.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class BLQuestionBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.put<BLQuestionController>(BLQuestionController(Get.find())); |
|||
} |
|||
|
|||
Future<void> deleteBindings() async { |
|||
await Future.wait([ |
|||
Get.delete<BLQuestionController>(), |
|||
]); |
|||
} |
|||
|
|||
Future<void> refreshBinding() async { |
|||
await deleteBindings(); |
|||
dependencies(); |
|||
} |
|||
} |
|||
@ -1,54 +0,0 @@ |
|||
import 'package:flutter/cupertino.dart'; |
|||
import 'package:shia_game_flutter/core/params/bl_question_params.dart'; |
|||
import 'package:shia_game_flutter/core/status/base_status.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/entity/bl_question_entity.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/domain/usecases/get_bl_question_usecase.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class BLQuestionController extends GetxController with StateMixin { |
|||
/// ----- Constructor ----- |
|||
BLQuestionController(this.getBLQuestionUseCase); |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
change('', status: RxStatus.success()); |
|||
} |
|||
|
|||
@override |
|||
void onClose() { |
|||
textEditingController.dispose(); |
|||
super.onClose(); |
|||
} |
|||
|
|||
/// ----- UseCases ----- |
|||
final GetBLQuestionUseCase getBLQuestionUseCase; |
|||
|
|||
/// ----- Variables ----- |
|||
final Rx<BLQuestionParams> bLQuestionParams = Rx(BLQuestionParams()); |
|||
final Rx<BLQuestionEntity> bLQuestionEntity = Rx(const BLQuestionEntity()); |
|||
|
|||
/// ------ Controllers ------ |
|||
final TextEditingController textEditingController = TextEditingController(); |
|||
|
|||
/// ------ Statuses ------ |
|||
final Rx<BaseStatus> getBLQuestionStatus = Rx(const BaseInit()); |
|||
|
|||
/// ------ Functions ------ |
|||
|
|||
/// ------ Api Calls ------ |
|||
Future<void> getBLQuestion() async { |
|||
change('', status: RxStatus.loading()); |
|||
await getBLQuestionUseCase(bLQuestionParams.value).then( |
|||
(value) => value.fold( |
|||
(data) { |
|||
bLQuestionEntity.value = data; |
|||
change('', status: RxStatus.success()); |
|||
}, |
|||
(error) { |
|||
change('', status: RxStatus.error(error.errorMessage)); |
|||
}, |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:shia_game_flutter/core/widgets/stepper/my_stepper.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/presentation/controller/bl_question_controller.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/presentation/ui/widgets/question_board.dart'; |
|||
import 'package:shia_game_flutter/features/battle_league/question_part/bl_question/presentation/ui/widgets/question_timer_ready_widget.dart'; |
|||
|
|||
class BLQuestionPage extends GetView<BLQuestionController> { |
|||
const BLQuestionPage({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return const Scaffold( |
|||
body: DecoratedBox( |
|||
decoration: BoxDecoration( |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [Color(0XFF390F72), Color(0XFF160C30)], |
|||
), |
|||
), |
|||
child: SafeArea( |
|||
child: Column( |
|||
children: [ |
|||
QuestionBoard(), |
|||
MyStepper(), |
|||
QuestionTimerReadyWidget(), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue