developer menu

This commit is contained in:
Christien Rioux 2023-10-09 16:52:37 -04:00
parent 8075b81742
commit ee94f6622c
41 changed files with 294 additions and 35 deletions

View File

@ -62,6 +62,7 @@ android {
signingConfig signingConfigs.debug
}
}
namespace 'com.veilid.veilidchat'
}
flutter {

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.veilid.veilidchat">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.veilid.veilidchat">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="VeilidChat"
android:name="${applicationName}"

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_round"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFFFF</color>
</resources>

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.veilid.veilidchat">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View File

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

Binary file not shown.

Binary file not shown.

View File

@ -159,5 +159,10 @@
"titlebar": "Settings",
"color_theme": "Color Theme",
"brightness_mode": "Brightness Mode"
},
"developer": {
"title": "Developer Logs",
"command": "Command",
"copied": "Selection copied"
}
}

Binary file not shown.

View File

@ -20,7 +20,6 @@ class VeilidChatApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
final localizationDelegate = LocalizedApp.of(context).delegate;
return ThemeProvider(

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
@ -83,10 +84,16 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
Future<void> Function({required Uint8List inviteData})
validateInviteData) {
final theme = Theme.of(context);
//final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
//final textTheme = theme.textTheme;
//final height = MediaQuery.of(context).size.height;
final monoStyle = TextStyle(
fontFamily: 'Source Code Pro',
fontSize: 11,
color: scale.primaryScale.text,
);
return Column(mainAxisSize: MainAxisSize.min, children: [
Text(
translate('paste_invite_dialog.paste_invite_here'),
@ -97,8 +104,7 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
enabled: !dialogState.isValidating,
onChanged: (text) async =>
_onPasteChanged(text, validateInviteData),
style: textTheme.labelSmall!
.copyWith(fontFamily: 'Victor Mono', fontSize: 11),
style: monoStyle,
keyboardType: TextInputType.multiline,
maxLines: null,
controller: _pasteTextController,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:signal_strength_indicator/signal_strength_indicator.dart';
import 'package:go_router/go_router.dart';
import '../providers/connection_state.dart';
import '../tools/tools.dart';
@ -48,13 +49,17 @@ class SignalStrengthMeterWidget extends ConsumerWidget {
}
inactiveColor = scale.grayScale.subtleText;
return SignalStrengthIndicator.bars(
value: value,
activeColor: color,
inactiveColor: inactiveColor,
size: iconSize,
barCount: 4,
spacing: 1,
);
return GestureDetector(
onLongPress: () async {
await context.push('/developer');
},
child: SignalStrengthIndicator.bars(
value: value,
activeColor: color,
inactiveColor: inactiveColor,
size: iconSize,
barCount: 4,
spacing: 1,
));
}
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'app.dart';
import 'providers/window_control.dart';
@ -36,13 +37,18 @@ void main() async {
// Make localization delegate
final delegate = await LocalizationDelegate.create(
fallbackLocale: 'en_US', supportedLocales: ['en_US']);
await initializeDateFormatting();
// Start up Veilid and Veilid processor in the background
unawaited(initializeVeilid());
// Run the app
// Hot reloads will only restart this part, not Veilid
runApp(ProviderScope(
observers: const [StateLogger()],
child: LocalizedApp(delegate, VeilidChatApp(theme: initTheme))));
runZonedGuarded(() {
runApp(ProviderScope(
observers: const [StateLogger()],
child: LocalizedApp(delegate, VeilidChatApp(theme: initTheme))));
}, (error, stackTrace) {
log.error('Dart Runtime: {$error}\n{$stackTrace}');
});
}

166
lib/pages/developer.dart Normal file
View File

@ -0,0 +1,166 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:go_router/go_router.dart';
import 'package:xterm/xterm.dart';
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
final globalDebugTerminal = Terminal(
maxLines: 50000,
);
const kDefaultTerminalStyle = TerminalStyle(
fontSize: 11,
// height: 1.2,
fontFamily: 'Source Code Pro');
class DeveloperPage extends ConsumerStatefulWidget {
const DeveloperPage({super.key});
@override
DeveloperPageState createState() => DeveloperPageState();
}
class DeveloperPageState extends ConsumerState<DeveloperPage> {
final terminalController = TerminalController();
final TextEditingController _debugCommandController = TextEditingController();
@override
void initState() {
// _scrollController = ScrollController(
// onAttach: _handlePositionAttach,
// onDetach: _handlePositionDetach,
// );
super.initState();
terminalController.addListener(() {
setState(() {});
});
}
// void _handleScrollChange() {
// if (_isScrolling != _scrollController.position.isScrollingNotifier.value) {
// _isScrolling = _scrollController.position.isScrollingNotifier.value;
// _wantsBottom = _scrollController.position.pixels ==
// _scrollController.position.maxScrollExtent;
// }
// }
// void _handlePositionAttach(ScrollPosition position) {
// // From here, add a listener to the given ScrollPosition.
// // Here the isScrollingNotifier will be used to inform when scrolling starts
// // and stops and change the AppBar's color in response.
// position.isScrollingNotifier.addListener(_handleScrollChange);
// }
// void _handlePositionDetach(ScrollPosition position) {
// // From here, add a listener to the given ScrollPosition.
// // Here the isScrollingNotifier will be used to inform when scrolling starts
// // and stops and change the AppBar's color in response.
// position.isScrollingNotifier.removeListener(_handleScrollChange);
// }
// void _scrollToBottom() {
// _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
// _wantsBottom = true;
// }
Future<void> _sendDebugCommand(String debugCommand) async {
log.info('DEBUG >>>\n$debugCommand');
final out = await Veilid.instance.debug(debugCommand);
log.info('<<< DEBUG\n$out');
}
Future<void> copySelection(BuildContext context) async {
final selection = terminalController.selection;
if (selection != null) {
final text = globalDebugTerminal.buffer.getText(selection);
terminalController.clearSelection();
await Clipboard.setData(ClipboardData(text: text));
if (context.mounted) {
showInfoToast(context, translate('developer.copied'));
}
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (!_isScrolling && _wantsBottom) {
// _scrollToBottom();
// }
// });
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: scale.primaryScale.text),
onPressed: () => GoRouterHelper(context).pop(),
),
actions: [
IconButton(
icon: const Icon(Icons.copy),
color: scale.primaryScale.text,
disabledColor: scale.grayScale.subtleText,
onPressed: terminalController.selection == null
? null
: () async {
await copySelection(context);
})
],
title: Text(translate('developer.title')),
centerTitle: true,
),
body: Column(children: [
TerminalView(
globalDebugTerminal,
textStyle: kDefaultTerminalStyle,
controller: terminalController,
//autofocus: true,
//backgroundOpacity: 0.9,
onSecondaryTapDown: (details, offset) async {
await copySelection(context);
},
).expanded(),
TextField(
controller: _debugCommandController,
decoration: InputDecoration(
filled: true,
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: scale.primaryScale.border)),
fillColor: scale.primaryScale.subtleBackground,
hintText: translate('developer.command'),
suffixIcon: IconButton(
icon: const Icon(Icons.send),
onPressed: () async {
final debugCommand = _debugCommandController.text;
_debugCommandController.clear();
await _sendDebugCommand(debugCommand);
},
)),
onSubmitted: (debugCommand) async {
_debugCommandController.clear();
await _sendDebugCommand(debugCommand);
},
).paddingAll(4)
]));
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<TerminalController>(
'terminalController', terminalController));
}
}

View File

@ -12,6 +12,13 @@ class IndexPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(windowControlProvider);
final theme = Theme.of(context);
final textTheme = theme.textTheme;
final monoTextStyle = textTheme.labelSmall!
.copyWith(fontFamily: 'Source Code Pro', fontSize: 11);
final emojiTextStyle = textTheme.labelSmall!
.copyWith(fontFamily: 'Noto Color Emoji', fontSize: 11);
return Scaffold(
body: DecoratedBox(
decoration: BoxDecoration(
@ -28,6 +35,11 @@ class IndexPage extends ConsumerWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Hack to preload fonts
Offstage(child: Text('🧱', style: emojiTextStyle)),
// Hack to preload fonts
Offstage(child: Text('A', style: monoTextStyle)),
// Splash Screen
Expanded(
flex: 2,
child: SvgPicture.asset(

View File

@ -21,8 +21,8 @@ import '../../entities/local_account.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../../veilid_support/veilid_support.dart';
import 'account_page.dart';
import 'chats_page.dart';
import 'account.dart';
import 'chats.dart';
class MainPager extends ConsumerStatefulWidget {
const MainPager(

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../pages/chat_only_page.dart';
import '../pages/chat_only.dart';
import '../pages/developer.dart';
import '../pages/home.dart';
import '../pages/index.dart';
import '../pages/new_account.dart';
@ -126,6 +127,10 @@ class RouterNotifier extends _$RouterNotifier implements Listenable {
),
],
),
GoRoute(
path: '/developer',
builder: (context, state) => const DeveloperPage(),
)
];
///////////////////////////////////////////////////////////////////////////

View File

@ -6,7 +6,7 @@ part of 'router_notifier.dart';
// RiverpodGenerator
// **************************************************************************
String _$routerNotifierHash() => r'b7c49cdc6f940c5a5032faaf19af08d9f478dae6';
String _$routerNotifierHash() => r'5a3527e3890f0746db4cbe051d453b89e5809989';
/// See also [RouterNotifier].
@ProviderFor(RouterNotifier)

View File

@ -2,8 +2,10 @@ import 'dart:io' show Platform;
import 'package:ansicolor/ansicolor.dart';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
import 'package:loggy/loggy.dart';
import '../pages/developer.dart';
import '../veilid_support/veilid_support.dart';
String wrapWithLogColor(LogLevel? level, String text) {
@ -47,14 +49,33 @@ String wrapWithLogColor(LogLevel? level, String text) {
return text;
}
final DateFormat _dateFormatter = DateFormat('HH:mm:ss.SSS');
extension PrettyPrintLogRecord on LogRecord {
String pretty() {
final lstr =
wrapWithLogColor(level, '[${level.toString().substring(0, 1)}]');
return '$lstr $message';
final tm = _dateFormatter.format(time.toLocal());
final lev = logEmoji(level);
final lstr = wrapWithLogColor(level, tm);
return '$lstr $lev $message';
}
}
String logEmoji(LogLevel logLevel) {
switch (logLevel) {
case traceLevel:
return '👾';
case LogLevel.debug:
return '🐛';
case LogLevel.info:
return '💡';
case LogLevel.warning:
return '⚠️';
case LogLevel.error:
return '🛑';
}
return '';
}
class CallbackPrinter extends LoggyPrinter {
CallbackPrinter() : super();
@ -62,7 +83,9 @@ class CallbackPrinter extends LoggyPrinter {
@override
void onLog(LogRecord record) {
debugPrint(record.pretty());
final out = record.pretty();
debugPrint(out);
globalDebugTerminal.write('$out\n'.replaceAll('\n', '\r\n'));
callback?.call(record);
}

View File

@ -36,7 +36,7 @@ void _initVeilid() {
const platformConfig = VeilidFFIConfig(
logging: VeilidFFIConfigLogging(
terminal: VeilidFFIConfigLoggingTerminal(
enabled: true,
enabled: false,
level: VeilidConfigLogLevel.debug,
),
otlp: VeilidFFIConfigLoggingOtlp(

View File

@ -20,6 +20,7 @@ Future<VeilidConfig> getVeilidChatConfig() async {
return config.copyWith(
capabilities: const VeilidConfigCapabilities(disable: ['DHTV', 'TUNL']),
protectedStore: config.protectedStore.copyWith(allowInsecureFallback: true),
// network: config.network.copyWith(
// dht: config.network.dht.copyWith(
// getValueCount: 3,

View File

@ -973,6 +973,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
platform_info:
dependency: transitive
description:
name: platform_info
sha256: "012e73712166cf0b56d3eb95c0d33491f56b428c169eca385f036448474147e4"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
plugin_platform_interface:
dependency: transitive
description:
@ -1601,6 +1609,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.3.0"
xterm:
dependency: "direct main"
description:
name: xterm
sha256: "6a02b15d03152b8186e12790902ff28c8a932fc441e89fa7255a7491661a8e69"
url: "https://pub.dev"
source: hosted
version: "3.5.0"
yaml:
dependency: transitive
description:

View File

@ -71,6 +71,7 @@ dependencies:
# veilid: ^0.0.1
path: ../veilid/veilid-flutter
window_manager: ^0.3.5
xterm: ^3.5.0
zxing2: ^0.2.0
dev_dependencies:
@ -120,9 +121,11 @@ flutter:
- assets/images/vlogo.svg
# Fonts
fonts:
- family: Victor Mono
- family: Source Code Pro
fonts:
- asset: assets/fonts/VictorMono-VariableFont_wght.ttf
- asset: assets/fonts/SourceCodePro-Regular.ttf
- asset: assets/fonts/SourceCodePro-Bold.ttf
weight: 700
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware