diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 423a1f59..5117ed97 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -208,6 +208,8 @@ PODS: - PromisesSwift (2.3.1): - PromisesObjC (= 2.3.1) - ReachabilitySwift (5.0.0) + - share_plus (0.0.1): + - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -237,6 +239,7 @@ DEPENDENCIES: - move_to_background (from `.symlinks/plugins/move_to_background/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) @@ -309,6 +312,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" url_launcher_ios: @@ -364,6 +369,7 @@ SPEC CHECKSUMS: PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f diff --git a/lib/main.dart b/lib/main.dart index a2b1d4a7..5a6335f5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,9 +11,11 @@ import 'package:get_storage/get_storage.dart'; import 'package:irllink/routes/app_pages.dart'; import 'package:irllink/src/bindings/login_bindings.dart'; import 'package:irllink/src/core/resources/themes.dart'; +import 'package:irllink/src/core/utils/crashlytics_talker_observer.dart'; import 'package:irllink/src/presentation/views/login_view.dart'; import 'package:kick_chat/kick_chat.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:talker_flutter/talker_flutter.dart'; import 'package:upgrader/upgrader.dart'; import 'package:wakelock/wakelock.dart'; import 'firebase_options.dart'; @@ -22,6 +24,11 @@ import 'src/core/utils/globals.dart' as globals; void main() async { WidgetsFlutterBinding.ensureInitialized(); + final crashlyticsTalkerObserver = CrashlyticsTalkerObserver(); + final talker = TalkerFlutter.init( + settings: TalkerSettings(), + observer: crashlyticsTalkerObserver, + ); await initializeService(); await GetStorage.init(); await Wakelock.enable(); @@ -34,8 +41,9 @@ void main() async { globals.buildNumber = packageInfo.buildNumber; globals.appName = packageInfo.appName; globals.packageName = packageInfo.packageName; + globals.talker = talker; AppTranslations.initLanguages(); - runApp(const Main()); + runApp(Main(talker: talker,)); } const notificationChannelId = 'irllink_foreground'; @@ -171,7 +179,11 @@ void notificationTapBackground(NotificationResponse notificationResponse) { } class Main extends StatelessWidget { - const Main({super.key}); + const Main({ + super.key, + required this.talker, + }); + final Talker talker; @override Widget build(BuildContext context) { @@ -190,6 +202,9 @@ class Main extends StatelessWidget { translations: AppTranslations(), locale: Get.deviceLocale, fallbackLocale: const Locale('en', 'US'), + navigatorObservers: [ + TalkerRouteObserver(talker), + ], ); } } diff --git a/lib/src/core/utils/crashlytics_talker_observer.dart b/lib/src/core/utils/crashlytics_talker_observer.dart new file mode 100644 index 00000000..2fe4d48f --- /dev/null +++ b/lib/src/core/utils/crashlytics_talker_observer.dart @@ -0,0 +1,24 @@ +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class CrashlyticsTalkerObserver extends TalkerObserver { + CrashlyticsTalkerObserver(); + + @override + void onError(err) { + FirebaseCrashlytics.instance.recordError( + err.error, + err.stackTrace, + reason: err.message, + ); + } + + @override + void onException(err) { + FirebaseCrashlytics.instance.recordError( + err.exception, + err.stackTrace, + reason: err.message, + ); + } +} \ No newline at end of file diff --git a/lib/src/core/utils/globals.dart b/lib/src/core/utils/globals.dart index d31bfb3b..f9828321 100644 --- a/lib/src/core/utils/globals.dart +++ b/lib/src/core/utils/globals.dart @@ -1,6 +1,9 @@ library globals; +import 'package:talker_flutter/talker_flutter.dart'; + String appName = "IRL Link"; String packageName = "com.irllink"; String version = "1.0.0"; String buildNumber = "1"; +Talker? talker; diff --git a/lib/src/core/utils/init_dio.dart b/lib/src/core/utils/init_dio.dart new file mode 100644 index 00000000..970eb610 --- /dev/null +++ b/lib/src/core/utils/init_dio.dart @@ -0,0 +1,21 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart'; +import 'package:talker_dio_logger/talker_dio_logger_settings.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; + +Dio initDio() { + final talker = globals.talker; + var dio = Dio(); + dio.interceptors.add( + TalkerDioLogger( + talker: talker, + settings: TalkerDioLoggerSettings( + requestFilter: (RequestOptions options) => kDebugMode ? true : !options.path.contains('api.twitch.tv'), + responseFilter: (response) => kDebugMode ? true : ![200, 202].contains(response.statusCode), + ), + ), + ); + + return dio; +} diff --git a/lib/src/core/utils/twitch_event_sub.dart b/lib/src/core/utils/twitch_event_sub.dart index 399d27b3..0a0c7ade 100644 --- a/lib/src/core/utils/twitch_event_sub.dart +++ b/lib/src/core/utils/twitch_event_sub.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; +import 'package:irllink/src/core/utils/init_dio.dart'; import 'package:irllink/src/data/entities/twitch/twitch_poll_dto.dart'; import 'package:irllink/src/data/entities/twitch/twitch_prediction_dto.dart'; import 'package:irllink/src/domain/entities/twitch/twitch_hype_train.dart'; @@ -11,6 +12,7 @@ import 'package:irllink/src/domain/entities/twitch/twitch_poll.dart'; import 'package:irllink/src/domain/entities/twitch/twitch_prediction.dart'; import 'package:twitch_chat/twitch_chat.dart'; import 'package:web_socket_channel/io.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; import '../../data/entities/twitch/twitch_hype_train_dto.dart'; import 'constants.dart'; @@ -137,13 +139,12 @@ class TwitchEventSub { } void _onDone() { - debugPrint("Twitch Sub Event: Connection closed"); + globals.talker?.debug("Twitch Sub Event: Connection closed"); close(); } void _onError(Object o, StackTrace s) { - debugPrint('Twitch event sub error: $o'); - debugPrint('Twitch event sub error: $s'); + globals.talker?.error("Twitch Sub Event: error", o, s); } Future _getChannelId() async { @@ -154,7 +155,7 @@ class TwitchEventSub { void subscribeToEvent(String type, String version, String sessionId, Map condition) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -167,7 +168,7 @@ class TwitchEventSub { "transport": {"method": "websocket", "session_id": sessionId} }); } on DioException catch (e) { - debugPrint(e.response.toString()); + globals.talker?.error(e.response.toString()); } } } diff --git a/lib/src/data/repositories/streamelements_repository_impl.dart b/lib/src/data/repositories/streamelements_repository_impl.dart index 1d83dc85..73b132fa 100644 --- a/lib/src/data/repositories/streamelements_repository_impl.dart +++ b/lib/src/data/repositories/streamelements_repository_impl.dart @@ -3,6 +3,7 @@ import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:irllink/src/core/params/streamelements_auth_params.dart'; import 'package:irllink/src/core/resources/data_state.dart'; +import 'package:irllink/src/core/utils/init_dio.dart'; import 'package:irllink/src/data/entities/stream_elements/se_activity_dto.dart'; import 'package:irllink/src/data/entities/stream_elements/se_me_dto.dart'; import 'package:irllink/src/data/entities/stream_elements/se_overlay_dto.dart'; @@ -49,7 +50,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { @override Future replayActivity(String token, SeActivity activity) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; await dio.post( @@ -63,7 +64,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { @override Future>> getLastActivities( String token, String channel) async { - var dio = Dio(); + var dio = initDio(); Response response; List activities = []; try { @@ -89,10 +90,6 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { ); return DataSuccess(activities); } on DioException catch (e) { - debugPrint(e.toString()); - debugPrint(e.response.toString()); - debugPrint(e.error.toString()); - return DataFailed(e.toString()); } } @@ -100,7 +97,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { @override Future>> getOverlays( String token, String channel) async { - var dio = Dio(); + var dio = initDio(); List overlays = []; try { dio.options.headers["Authorization"] = "Bearer $token"; @@ -115,14 +112,13 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { ); return DataSuccess(overlays); } on DioException catch (e) { - debugPrint(e.toString()); return DataFailed(e.toString()); } } @override Future> getMe(String token) async { - var dio = Dio(); + var dio = initDio(); late SeMe me; try { dio.options.headers["Authorization"] = "Bearer $token"; @@ -134,14 +130,13 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { return DataSuccess(me); } on DioException catch (e) { - debugPrint(e.toString()); return DataFailed(e.toString()); } } @override Future nextSong(String token, String userId) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; await dio.post( @@ -154,7 +149,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { @override Future removeSong(String token, String userId, String songId) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; await dio.delete( @@ -167,7 +162,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { @override Future resetQueue(String token, String userId) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; await dio.delete( @@ -182,7 +177,7 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { Future>> getSongQueue( String token, String userId) async { List songs = []; - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; Response response = await dio.get( @@ -203,14 +198,13 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { return DataSuccess(songs); } on DioException catch (e) { - debugPrint(e.toString()); return DataFailed(e.toString()); } } @override Future> getSongPlaying(String token, String userId) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; Response response = await dio.get( @@ -227,14 +221,13 @@ class StreamelementsRepositoryImpl extends StreamelementsRepository { return DataSuccess(song); } on DioException catch (e) { - debugPrint(e.toString()); return DataFailed(e.toString()); } } @override Future updatePlayerState(String token, String userId, String state) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers["Authorization"] = "Bearer $token"; await dio.post( diff --git a/lib/src/data/repositories/twitch_repository_impl.dart b/lib/src/data/repositories/twitch_repository_impl.dart index 1369d6af..77b0c3d2 100644 --- a/lib/src/data/repositories/twitch_repository_impl.dart +++ b/lib/src/data/repositories/twitch_repository_impl.dart @@ -9,6 +9,7 @@ import 'package:get_storage/get_storage.dart'; import 'package:irllink/src/core/params/twitch_auth_params.dart'; import 'package:irllink/src/core/resources/data_state.dart'; import 'package:irllink/src/core/utils/constants.dart'; +import 'package:irllink/src/core/utils/init_dio.dart'; import 'package:irllink/src/data/entities/twitch/twitch_credentials_dto.dart'; import 'package:irllink/src/data/entities/twitch/twitch_decoded_idtoken_dto.dart'; import 'package:irllink/src/data/entities/twitch/twitch_stream_infos_dto.dart'; @@ -100,7 +101,7 @@ class TwitchRepositoryImpl extends TwitchRepository { TwitchCredentials twitchData, ) async { Response response; - var dio = Dio(); + var dio = initDio(); try { final remoteConfig = FirebaseRemoteConfig.instance; await remoteConfig.fetchAndActivate(); @@ -109,7 +110,11 @@ class TwitchRepositoryImpl extends TwitchRepository { response = await dio.get( apiRefreshTokenUrl, - queryParameters: {'refresh_token': twitchData.refreshToken, 'app_version': globals.version, 'platform': Platform.isAndroid ? 'android' : 'ios'}, + queryParameters: { + 'refresh_token': twitchData.refreshToken, + 'app_version': globals.version, + 'platform': Platform.isAndroid ? 'android' : 'ios' + }, ); TwitchCredentials newTwitchData = TwitchCredentials( @@ -127,21 +132,19 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(newTwitchData); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Refresh encountered issues"); + return DataFailed(e.toString()); } } Future validateToken(String accessToken) async { try { Response response; - var dio = Dio(); + var dio = initDio(); dio.options.headers["authorization"] = "Bearer $accessToken"; response = await dio.get('https://id.twitch.tv/oauth2/validate'); return response.data; } on DioException catch (e) { - debugPrint(e.response.toString()); - return "error"; + return DataFailed(e.toString()); } } @@ -150,7 +153,7 @@ class TwitchRepositoryImpl extends TwitchRepository { final box = GetStorage(); box.remove('twitchData'); Response response; - var dio = Dio(); + var dio = initDio(); try { response = await dio.post( 'https://id.twitch.tv/oauth2/revoke', @@ -161,9 +164,8 @@ class TwitchRepositoryImpl extends TwitchRepository { ); return DataSuccess(response.toString()); } on DioException catch (e) { - debugPrint(e.toString()); + return DataFailed(e.toString()); } - return const DataSuccess('Logged out successfuly'); } @override @@ -215,7 +217,7 @@ class TwitchRepositoryImpl extends TwitchRepository { Future> getTwitchUser( String? username, String accessToken) async { Response response; - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -237,8 +239,7 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(twitchUser); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Error retrieving user infos"); + return DataFailed(e.toString()); } } @@ -246,7 +247,7 @@ class TwitchRepositoryImpl extends TwitchRepository { Future>> getTwitchUsers( List ids, String accessToken) async { Response response; - var dio = Dio(); + var dio = initDio(); List twitchUsers = []; try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; @@ -271,8 +272,7 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(twitchUsers); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Error retrieving users infos"); + return DataFailed(e.toString()); } } @@ -281,7 +281,7 @@ class TwitchRepositoryImpl extends TwitchRepository { String accessToken, String broadcasterId) async { Response response; Response response2; - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -304,8 +304,7 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(twitchStreamInfosDto); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Error Getting Stream Infos"); + return DataFailed(e.toString()); } } @@ -314,7 +313,7 @@ class TwitchRepositoryImpl extends TwitchRepository { String broadcasterId, TwitchStreamInfos? twitchStreamInfos) async { Response response; Map settings = {}; - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -345,15 +344,14 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(response); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Error editing Stream chat settings"); + return DataFailed(e.toString()); } } @override Future> setStreamTitle( String accessToken, String broadcasterId, String title) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -368,8 +366,7 @@ class TwitchRepositoryImpl extends TwitchRepository { ); return const DataSuccess(null); } on DioException catch (e) { - debugPrint(e.toString()); - return const DataFailed("Error editing Stream chat settings"); + return DataFailed(e.toString()); } } @@ -381,7 +378,7 @@ class TwitchRepositoryImpl extends TwitchRepository { String status, String? winningOutcomeId, ) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; @@ -401,15 +398,14 @@ class TwitchRepositoryImpl extends TwitchRepository { return const DataSuccess(""); } on DioException catch (e) { - debugPrint(e.response.toString()); - return const DataFailed("Error ending prediction"); + return DataFailed(e.toString()); } } @override Future> endPoll(String accessToken, String broadcasterId, String pollId, String status) async { - var dio = Dio(); + var dio = initDio(); Response response; TwitchPoll? poll; @@ -432,8 +428,7 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(poll); } on DioException catch (e) { - debugPrint(e.response.toString()); - return const DataFailed("Error ending poll"); + return DataFailed(e.toString()); } } @@ -441,7 +436,7 @@ class TwitchRepositoryImpl extends TwitchRepository { Future> createPoll( String accessToken, String broadcasterId, TwitchPoll newPoll) async { // Response response; - var dio = Dio(); + var dio = initDio(); // TwitchPrediction? prediction; try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; @@ -451,8 +446,7 @@ class TwitchRepositoryImpl extends TwitchRepository { return DataSuccess(newPoll); } on DioException catch (e) { - debugPrint(e.response.toString()); - return const DataFailed("Error retrieving Twitch Prediction"); + return DataFailed(e.toString()); } } @@ -463,7 +457,7 @@ class TwitchRepositoryImpl extends TwitchRepository { ChatMessage message, int? duration, ) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; @@ -495,7 +489,7 @@ class TwitchRepositoryImpl extends TwitchRepository { String broadcasterId, ChatMessage message, ) async { - var dio = Dio(); + var dio = initDio(); try { dio.options.headers['Client-Id'] = kTwitchAuthClientId; dio.options.headers["authorization"] = "Bearer $accessToken"; diff --git a/lib/src/presentation/controllers/chat_view_controller.dart b/lib/src/presentation/controllers/chat_view_controller.dart index 47bb910b..01af5cb8 100644 --- a/lib/src/presentation/controllers/chat_view_controller.dart +++ b/lib/src/presentation/controllers/chat_view_controller.dart @@ -12,6 +12,7 @@ import 'package:irllink/src/presentation/controllers/tts_controller.dart'; import 'package:irllink/src/presentation/events/home_events.dart'; import 'package:kick_chat/kick_chat.dart'; import 'package:twitch_chat/twitch_chat.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; import 'home_view_controller.dart'; import 'package:irllink/src/domain/entities/chat/chat_message.dart' as entity; @@ -378,7 +379,7 @@ class ChatViewController extends GetxController kc.channel, onDone: () => {}, onError: () => { - debugPrint('error on kick chat'), + globals.talker?.error('error on kick chat'), }, ); await kickChat.connect(); diff --git a/lib/src/presentation/controllers/obs_tab_view_controller.dart b/lib/src/presentation/controllers/obs_tab_view_controller.dart index 903b618c..91c9334c 100644 --- a/lib/src/presentation/controllers/obs_tab_view_controller.dart +++ b/lib/src/presentation/controllers/obs_tab_view_controller.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:irllink/src/presentation/events/home_events.dart'; import 'package:obs_websocket/obs_websocket.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; import 'home_view_controller.dart'; @@ -140,7 +141,7 @@ class ObsTabViewController extends GetxController { final StatsResponse statsResponse = await obsWebSocket!.general.getStats(); - debugPrint(statsResponse.toString()); + globals.talker?.debug(statsResponse.toString()); } catch (e) { alertMessage.value = "Failed to connect to OBS"; isConnected.value = false; @@ -148,7 +149,8 @@ class ObsTabViewController extends GetxController { } void connectionLost() { - debugPrint("connectionLost"); + globals.talker?.error("connection lost"); + isConnected.value = false; alertMessage.value = "Connection with OBS lost..."; } diff --git a/lib/src/presentation/controllers/store_controller.dart b/lib/src/presentation/controllers/store_controller.dart index 1ad398ac..fb4da4a8 100644 --- a/lib/src/presentation/controllers/store_controller.dart +++ b/lib/src/presentation/controllers/store_controller.dart @@ -1,12 +1,14 @@ import 'dart:async'; import 'dart:io'; -import 'package:dio/dio.dart' as dio_l; +import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:irllink/src/core/utils/init_dio.dart'; import 'package:irllink/src/presentation/controllers/home_view_controller.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; class StoreController extends GetxController { late StreamSubscription> subscription; @@ -72,7 +74,7 @@ class StoreController extends GetxController { try { await InAppPurchase.instance.restorePurchases(); } catch (error) { - debugPrint('not logged to any store'); + globals.talker?.error('Not logged to any store.'); } } @@ -120,7 +122,7 @@ class StoreController extends GetxController { String? pruchaseToken = purchaseDetails.verificationData.serverVerificationData; HomeViewController homeViewController = Get.find(); - var dio = dio_l.Dio(); + var dio = initDio(); try { await dio.post( 'https://www.irllink.com/api/verify-android-purchase', @@ -130,8 +132,8 @@ class StoreController extends GetxController { }, ); return Future.value(true); - } on dio_l.DioException catch (e) { - debugPrint(e.toString()); + } on DioException catch (e) { + globals.talker?.error(e.toString()); return Future.value(false); } } diff --git a/lib/src/presentation/controllers/streamelements_view_controller.dart b/lib/src/presentation/controllers/streamelements_view_controller.dart index fc43abb6..b33ed111 100644 --- a/lib/src/presentation/controllers/streamelements_view_controller.dart +++ b/lib/src/presentation/controllers/streamelements_view_controller.dart @@ -9,6 +9,7 @@ import 'package:irllink/src/domain/entities/stream_elements/se_overlay.dart'; import 'package:irllink/src/domain/entities/stream_elements/se_song.dart'; import 'package:irllink/src/presentation/events/streamelements_events.dart'; import 'package:socket_io_client/socket_io_client.dart'; +import 'package:irllink/src/core/utils/globals.dart' as globals; import 'home_view_controller.dart'; @@ -198,17 +199,19 @@ class StreamelementsViewController extends GetxController Future onError() async { isSocketConnected.value = false; - debugPrint('Error connecting to StreamElements websocket'); + globals.talker?.error('Error on StreamElements websocket'); + } Future onDisconnect() async { isSocketConnected.value = false; - debugPrint('Disconnected from StreamElements websocket'); + globals.talker?.warning('Disconnected from StreamElements websocket'); } Future onAuthenticated(data) async { isSocketConnected.value = true; - debugPrint('Connected to StreamElements websocket'); + globals.talker?.info('Connected to StreamElements websocket'); + } void onAddSongQueue(data) { @@ -335,7 +338,6 @@ class StreamelementsViewController extends GetxController String type = event["type"]; switch (type) { case "follower": - debugPrint(event.toString()); if (!homeViewController.settings.value.streamElementsSettings! .showFollowerActivity) return; SeActivity activity = SeActivity( @@ -365,7 +367,6 @@ class StreamelementsViewController extends GetxController case "tip": if (!homeViewController.settings.value.streamElementsSettings! .showDonationActivity) return; - debugPrint(event.toString()); SeActivity activity = SeActivity( id: event["_id"], channel: event["channel"], diff --git a/lib/src/presentation/views/settings_view.dart b/lib/src/presentation/views/settings_view.dart index 993813e4..3f968333 100644 --- a/lib/src/presentation/views/settings_view.dart +++ b/lib/src/presentation/views/settings_view.dart @@ -7,6 +7,7 @@ import 'package:irllink/src/presentation/controllers/store_controller.dart'; import 'package:irllink/src/presentation/widgets/settings/chat_events.dart'; import 'package:irllink/src/presentation/widgets/settings/dashboard_settings_view.dart'; import 'package:irllink/src/presentation/widgets/settings/stream_elements.dart'; +import 'package:irllink/src/presentation/widgets/settings/talker_screen.dart'; import 'package:irllink/src/presentation/widgets/settings/tts.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:irllink/src/core/utils/globals.dart' as globals; @@ -121,6 +122,18 @@ class SettingsView extends GetView { padding: const EdgeInsets.only(left: 4, right: 4, top: 6), child: Text("Version: ${globals.version}"), ), + settingsGoToRow( + context, + 'Logs', + Icons.list_alt, + () { + Get.to( + () => TalkerScreenView( + talker: globals.talker!, + ), + ); + }, + ), ], ), ), diff --git a/lib/src/presentation/widgets/settings/talker_screen.dart b/lib/src/presentation/widgets/settings/talker_screen.dart new file mode 100644 index 00000000..18607d84 --- /dev/null +++ b/lib/src/presentation/widgets/settings/talker_screen.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:get/get_state_manager/src/simple/get_view.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class TalkerScreenView extends GetView { + final Talker talker; + + const TalkerScreenView({ + super.key, + required this.talker, + }); + + @override + Widget build(BuildContext context) { + return TalkerScreen( + appBarTitle: 'Logs', + talker: talker, + theme: const TalkerScreenTheme( + backgroundColor: Color(0xFF0e0e10), + cardColor: Color(0xFF18181b), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 5a498bec..4a80f278 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.30" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" + url: "https://pub.dev" + source: hosted + version: "2.0.2" api_7tv: dependency: transitive description: @@ -169,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + url: "https://pub.dev" + source: hosted + version: "0.3.4+1" crypto: dependency: transitive description: @@ -536,6 +552,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + group_button: + dependency: transitive + description: + name: group_button + sha256: "0610fcf28ed122bfb4b410fce161a390f7f2531d55d1d65c5375982001415940" + url: "https://pub.dev" + source: hosted + version: "5.3.4" html: dependency: transitive description: @@ -728,6 +752,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" mobile_scanner: dependency: "direct main" description: @@ -912,6 +944,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + share_plus: + dependency: transitive + description: + name: share_plus + sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544 + url: "https://pub.dev" + source: hosted + version: "9.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4" + url: "https://pub.dev" + source: hosted + version: "4.0.0" shared_preferences: dependency: transitive description: @@ -1037,6 +1085,38 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0+1" + talker: + dependency: transitive + description: + name: talker + sha256: edd1ec2d1e941ac1569f27d820cd896e0dc9bc4e4160963e3c8c192079f08da0 + url: "https://pub.dev" + source: hosted + version: "4.2.0" + talker_dio_logger: + dependency: "direct main" + description: + name: talker_dio_logger + sha256: ff10ae1294383cdc50f3b7432a6ca623d7634cf0f3c669d8897f948a06c3fd56 + url: "https://pub.dev" + source: hosted + version: "4.2.0" + talker_flutter: + dependency: "direct main" + description: + name: talker_flutter + sha256: "8b10ea148e16410abc950ab596a9cb9b2ae4380640dec9a88dc80503f41902b8" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + talker_logger: + dependency: transitive + description: + name: talker_logger + sha256: "777c62bb8019ec26f5b99626fe0bd28e169bae724ffde8438a5e4ad28e2c20df" + url: "https://pub.dev" + source: hosted + version: "4.2.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b2dc8930..2f8143e5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,9 @@ dependencies: flutter_colorpicker: ^1.0.3 instant: ^0.5.1 uuid: ^3.0.7 - + talker_flutter: ^4.2.0 + talker_dio_logger: ^4.2.0 + dependency_overrides: wakelock_windows: git: diff --git a/test/widget_test.dart b/test/widget_test.dart index 80edc189..2b3c29cc 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -9,11 +9,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:irllink/main.dart'; +import 'package:talker_flutter/talker_flutter.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { + final talker = TalkerFlutter.init( + settings: TalkerSettings(), + ); // Build our app and trigger a frame. - await tester.pumpWidget(const Main()); + await tester.pumpWidget(Main(talker: talker)); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);