Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: logger #524

Merged
merged 11 commits into from
Jan 9, 2025
14 changes: 13 additions & 1 deletion android/app/src/main/kotlin/io/ion/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,27 @@ package io.ion.app
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.banuba.sdk.pe.PhotoCreationActivity
import android.view.KeyEvent
import com.banuba.sdk.pe.BanubaPhotoEditor
import com.banuba.sdk.pe.PhotoCreationActivity
import com.banuba.sdk.pe.data.PhotoEditorConfig
import dev.fluttercommunity.shake_gesture_android.ShakeGesturePlugin
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import java.io.File

class MainActivity : FlutterActivity() {
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_MENU) {
this.flutterEngine?.plugins?.get(ShakeGesturePlugin::class.java).let { plugin ->
if (plugin is ShakeGesturePlugin)
plugin.onShake()
}
}

return super.onKeyDown(keyCode, event)
}

companion object {
// For Photo Editor
Expand Down
12 changes: 9 additions & 3 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ PODS:
- SDWebImage (5.20.0):
- SDWebImage/Core (= 5.20.0)
- SDWebImage/Core (5.20.0)
- shake_gesture_ios (0.0.1):
- Flutter
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
Expand Down Expand Up @@ -241,6 +243,7 @@ DEPENDENCIES:
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`)
- quill_native_bridge (from `.symlinks/plugins/quill_native_bridge/ios`)
- shake_gesture_ios (from `.symlinks/plugins/shake_gesture_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
Expand Down Expand Up @@ -333,6 +336,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/qr_code_scanner/ios"
quill_native_bridge:
:path: ".symlinks/plugins/quill_native_bridge/ios"
shake_gesture_ios:
:path: ".symlinks/plugins/shake_gesture_ios/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
Expand Down Expand Up @@ -400,9 +405,10 @@ SPEC CHECKSUMS:
qr_code_scanner: d77f94ecc9abf96d9b9b8fc04ef13f611e5a147a
quill_native_bridge: fd2819cf6da02fb6cbf9de37835f96e798e145eb
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
shake_gesture_ios: 64f1f579f314c58445761992a123111b3d7b3492
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
sqlite3: 7559e33dae4c78538df563795af3a86fc887ee71
sqlite3_flutter_libs: f0b59f6bb2a18597d0796558725007e5a7428397
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class DiscoverCreators extends HookConsumerWidget {
final contentCreators = entitiesPagedData?.data.items?.whereType<UserMetadataEntity>();

final hideCreatorsWithoutPicture = ref
.watch(featureFlagsProvider.notifier)
.read(featureFlagsProvider.notifier)
.get(HideCreatorsWithoutPicture.hideCreatorsWithoutPicture);

final filteredCreators = hideCreatorsWithoutPicture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ class ForceUpdate extends _$ForceUpdate {
}

Future<void> _checkAndUpdateConfig() async {
await ref.read(envProvider.future);

final refetchIntervalInMilliseconds =
ref.read(envProvider.notifier).get<int>(EnvVariable.VERSIONS_CONFIG_REFETCH_INTERVAL);

Expand Down
9 changes: 9 additions & 0 deletions lib/app/features/core/model/feature_flags.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ final class FeedFeatureFlag extends FeatureFlag {
static const showMentionsSuggestions = FeedFeatureFlag._(key: 'showMentionsSuggestions');
}

final class LoggerFeatureFlag extends FeatureFlag {
const LoggerFeatureFlag._({required super.key});

static const logApp = FeedFeatureFlag._(key: 'logApp');
static const logRouters = FeedFeatureFlag._(key: 'logRouters');
static const logNostrDart = FeedFeatureFlag._(key: 'logNostrDart');
static const logIonIdentityClient = FeedFeatureFlag._(key: 'logIonIdentityClient');
}

///
/// TODO: remove this once before production release
/// It hides creators without picture from the discover creators page
Expand Down
11 changes: 7 additions & 4 deletions lib/app/features/core/providers/dio_provider.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ion/app/services/logger/config.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'dio_provider.c.g.dart';

@riverpod
Dio dio(Ref ref) {
final dio = Dio();
if (LoggerConfig.dioLogsEnabled) {
dio.interceptors.add(PrettyDioLogger());

final logger = Logger.talkerDioLogger;

if (logger != null) {
dio.interceptors.add(logger);
}

return dio;
}
37 changes: 10 additions & 27 deletions lib/app/features/core/providers/env_provider.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// ignore_for_file: constant_identifier_names

import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:ion/generated/assets.gen.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'env_provider.c.g.dart';
Expand All @@ -21,35 +20,19 @@ enum EnvVariable {
@Riverpod(keepAlive: true)
class Env extends _$Env {
@override
Future<void> build() async {
await dotenv.load(fileName: Assets.aApp);
final notDefined = _getNotDefined();
if (notDefined.isNotEmpty) {
throw Exception('Invalid ENV value for $notDefined');
}
}

List<EnvVariable> _getNotDefined() {
return EnvVariable.values
.where((EnvVariable element) => dotenv.maybeGet(element.name) == null)
.toList();
}
void build() {}

/// Gets a typed environment variable value.
/// Throws if the variable is not found or cannot be converted to type [T].
T get<T>(EnvVariable variable) {
final value = dotenv.get(variable.name);

if (T == bool) {
return (value.toLowerCase() == 'true') as T;
}

if (T == int) {
return int.parse(value) as T;
}

if (T == double) {
return double.parse(value) as T;
}

return value as T;
return switch (T) {
bool => (value.toLowerCase() == 'true') as T,
int => int.parse(value) as T,
double => double.parse(value) as T,
String => value as T,
_ => throw Exception('Unsupported type $T'),
};
}
}
63 changes: 15 additions & 48 deletions lib/app/features/core/providers/feature_flags_provider.c.dart
Original file line number Diff line number Diff line change
@@ -1,64 +1,31 @@
// SPDX-License-Identifier: ice License 1.0

import 'package:ion/app/features/core/model/feature_flags.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:ion/app/features/core/providers/env_provider.c.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'feature_flags_provider.c.g.dart';

abstract class FeatureFlagsService {
const FeatureFlagsService();

bool? get(FeatureFlag flag);

Set<FeatureFlag> get supportedFlags;
}

final class LocalFeatureFlagsService extends FeatureFlagsService {
factory LocalFeatureFlagsService() {
return const LocalFeatureFlagsService._({
@Riverpod(keepAlive: true)
class FeatureFlags extends _$FeatureFlags {
@override
Map<FeatureFlag, bool> build() {
return {
/// Local flags
WalletFeatureFlag.buyNftEnabled: false,
FeedFeatureFlag.showTrendingVideo: false,
FeedFeatureFlag.showMentionsSuggestions: false,
HideCreatorsWithoutPicture.hideCreatorsWithoutPicture: true,
});
}

const LocalFeatureFlagsService._(this._featuresMap);

final Map<FeatureFlag, bool> _featuresMap;

@override
bool? get(FeatureFlag flag) => _featuresMap[flag];

@override
Set<FeatureFlag> get supportedFlags => _featuresMap.keys.toSet();
}

@Riverpod(keepAlive: true)
class FeatureFlags extends _$FeatureFlags {
late final Set<FeatureFlagsService> _services;

@override
Future<void> build() async {
_services = {
LocalFeatureFlagsService(),
/// Log flags
if (ref.watch(envProvider.notifier).get(EnvVariable.SHOW_DEBUG_INFO)) ...{
LoggerFeatureFlag.logApp: true,
LoggerFeatureFlag.logRouters: false,
LoggerFeatureFlag.logNostrDart: true,
LoggerFeatureFlag.logIonIdentityClient: true,
},
};
}

bool get(FeatureFlag flag, {bool defaultValue = false}) {
for (final service in _services) {
if (service.supportedFlags.contains(flag)) {
final value = service.get(flag);

if (value != null) {
return value;
}
}
}

Logger.log('${flag.key} not found.');

return defaultValue;
}
bool get(FeatureFlag flag) => state[flag] ?? false;
}
20 changes: 12 additions & 8 deletions lib/app/features/core/providers/init_provider.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,37 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ion/app/features/auth/providers/auth_provider.c.dart';
import 'package:ion/app/features/auth/providers/onboarding_complete_provider.c.dart';
import 'package:ion/app/features/core/model/feature_flags.dart';
import 'package:ion/app/features/core/permissions/providers/permissions_provider.c.dart';
import 'package:ion/app/features/core/providers/env_provider.c.dart';
import 'package:ion/app/features/core/providers/feature_flags_provider.c.dart';
import 'package:ion/app/features/core/providers/template_provider.c.dart';
import 'package:ion/app/features/core/providers/window_manager_provider.c.dart';
import 'package:ion/app/features/wallet/data/coins/domain/coin_initializer.c.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:ion/app/services/nostr/nostr.dart';
import 'package:ion/app/services/nostr/nostr_logger.dart';
import 'package:ion/app/services/storage/local_storage.c.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'init_provider.c.g.dart';

@Riverpod(keepAlive: true)
Future<void> initApp(Ref ref) async {
Nostr.initialize();
final featureFlagsNotifier = ref.read(featureFlagsProvider.notifier);
final logApp = featureFlagsNotifier.get(LoggerFeatureFlag.logApp);
final logNostrDart = featureFlagsNotifier.get(LoggerFeatureFlag.logNostrDart);

if (logApp) Logger.init();

Nostr.initialize(logNostrDart ? NostrLogger() : null);

await Future.wait([
ref.read(windowManagerProvider.notifier).show(),
ref.read(envProvider.future),
ref.read(sharedPreferencesProvider.future),
]);

await Future.wait([
ref.read(appTemplateProvider.future),
ref.read(authProvider.future),
ref.read(permissionsProvider.notifier).checkAllPermissions(),
ref.read(coinInitializerProvider).initialize(),
ref.read(onboardingCompleteProvider.future),
]);

await ref.read(onboardingCompleteProvider.future);
}
5 changes: 4 additions & 1 deletion lib/app/features/core/views/pages/error_modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import 'package:ion/app/components/screen_offset/screen_side_offset.dart';
import 'package:ion/app/exceptions/exceptions.dart';
import 'package:ion/app/extensions/extensions.dart';
import 'package:ion/app/features/core/providers/env_provider.c.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:ion/generated/assets.gen.dart';

class ErrorModal extends ConsumerWidget {
const ErrorModal({required this.error, super.key});
ErrorModal({required this.error, super.key}) {
Logger.error(error);
}

final Object error;

Expand Down
75 changes: 75 additions & 0 deletions lib/app/features/debug/views/debug_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: ice License 1.0

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ion/app/extensions/extensions.dart';
import 'package:ion/app/features/core/providers/feature_flags_provider.c.dart';
import 'package:ion/app/router/components/navigation_app_bar/navigation_app_bar.dart';
import 'package:ion/app/router/components/navigation_app_bar/navigation_close_button.dart';
import 'package:ion/app/services/logger/logger.dart';
import 'package:talker_flutter/talker_flutter.dart';

class DebugPage extends ConsumerWidget {
const DebugPage({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final featureFlags = ref.watch(featureFlagsProvider);
final talker = Logger.talker;

return Column(
mainAxisSize: MainAxisSize.min,
children: [
NavigationAppBar.modal(
title: const Text('🐞 Debug'),
showBackButton: false,
actions: const [NavigationCloseButton()],
),
Flexible(
child: Padding(
padding: const EdgeInsets.all(16),
child: ListView(
shrinkWrap: true,
children: [
if (talker != null)
Card(
child: ListTile(
leading: const Icon(Icons.bug_report),
title: const Text('View Debug Logs'),
subtitle: const Text('Check application logs and diagnostics'),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.of(context).push(
MaterialPageRoute<TalkerScreen>(
builder: (context) => TalkerScreen(
talker: talker,
),
),
),
),
),
SizedBox(height: 16.0.s),
ExpansionTile(
title: const Text('Feature Flags'),
children: featureFlags.entries
.map(
(entry) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: ListTile(
title: Text(entry.key.key),
trailing: Icon(
entry.value ? Icons.check_circle : Icons.cancel,
color: entry.value ? Colors.green : Colors.red,
),
),
),
)
.toList(),
),
],
),
),
),
],
);
}
}
Loading