Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 61 additions & 52 deletions app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:stride/routes/logging_routes.dart';
import 'package:stride/routes/tasks_route.dart';
import 'package:stride/theme.dart';
import 'package:stride/utils/functions.dart';
import 'package:feedback/feedback.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand Down Expand Up @@ -46,7 +47,7 @@ Future<void> main() async {
);
}

class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
final Settings settings;
final PluginManagerState pluginManagerState;
const MyApp({
Expand All @@ -55,6 +56,11 @@ class MyApp extends StatelessWidget {
required this.pluginManagerState,
});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
Expand All @@ -63,7 +69,7 @@ class MyApp extends StatelessWidget {
BlocProvider<LogBloc>(create: (context) => LogBloc()),
BlocProvider<SettingsBloc>(
create: (context) => SettingsBloc(
settings: settings,
settings: widget.settings,
logBloc: context.read<LogBloc>(),
),
),
Expand All @@ -77,7 +83,7 @@ class MyApp extends StatelessWidget {
BlocProvider<PluginManagerBloc>(
create: (context) => PluginManagerBloc(
logBloc: context.read<LogBloc>(),
state: pluginManagerState,
state: widget.pluginManagerState,
taskBloc: context.read<TaskBloc>(),
),
lazy: false,
Expand All @@ -91,59 +97,62 @@ class MyApp extends StatelessWidget {
},
child: BlocBuilder<SettingsBloc, SettingsState>(
builder: (context, state) {
return MaterialApp(
title: 'Stride',
theme: generateTheme(darkMode: false),
darkTheme: generateTheme(darkMode: true),
themeMode:
state.settings.darkMode ? ThemeMode.dark : ThemeMode.light,
home: BlocListener<LogBloc, LogState>(
listener: (context, state) {
if (!state.show) {
return;
}
return BetterFeedback(
darkTheme: FeedbackThemeData.dark(),
child: MaterialApp(
title: 'Stride',
theme: generateTheme(darkMode: false),
darkTheme: generateTheme(darkMode: true),
themeMode:
state.settings.darkMode ? ThemeMode.dark : ThemeMode.light,
home: BlocListener<LogBloc, LogState>(
listener: (context, state) {
if (!state.show) {
return;
}

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message.split('\n')[0]),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 10),
backgroundColor: state.isError ? Colors.red[300] : null,
action: SnackBarAction(
label: 'Go to Logs',
onPressed: () async {
// TODO: Maybe don't push if already there on top.
await Navigator.of(context).push<void>(
MaterialPageRoute(
builder: (context) => const LoggingRoute(),
),
);
},
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message.split('\n')[0]),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 10),
backgroundColor: state.isError ? Colors.red[300] : null,
action: SnackBarAction(
label: 'Go to Logs',
onPressed: () async {
// TODO: Maybe don't push if already there on top.
await Navigator.of(context).push<void>(
MaterialPageRoute(
builder: (context) => const LoggingRoute(),
),
);
},
),
),
),
);
},
child: BlocListener<DialogBloc, DialogState>(
listener: (context, state) async {
final title = await state.title(context);
if (!context.mounted) return;
final description = await state.content?.call(context);
if (!context.mounted) return;
showAlertDialog(
context: context,
content: description == null
? title
: Column(
mainAxisSize: MainAxisSize.min,
children: [title, description],
),
onConfirm: state.onConfirm,
onCancel: state.onCancel,
);
},
child: settings.repositories.isEmpty
? const InitialRoute()
: const TasksRoute(),
child: BlocListener<DialogBloc, DialogState>(
listener: (context, state) async {
final title = await state.title(context);
if (!context.mounted) return;
final description = await state.content?.call(context);
if (!context.mounted) return;
showAlertDialog(
context: context,
content: description == null
? title
: Column(
mainAxisSize: MainAxisSize.min,
children: [title, description],
),
onConfirm: state.onConfirm,
onCancel: state.onCancel,
);
},
child: widget.settings.repositories.isEmpty
? const InitialRoute()
: const TasksRoute(),
),
),
),
);
Expand Down
57 changes: 56 additions & 1 deletion app/lib/routes/settings_route.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:feedback/feedback.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand All @@ -11,6 +12,44 @@ import 'package:stride/routes/ssh_keys_route.dart';
import 'package:stride/widgets/settings_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';

/// Shows an [AlertDialog] with the given feedback.
/// This is useful for debugging purposes.
void alertFeedbackFunction(
BuildContext outerContext,
UserFeedback feedback,
) {
showDialog<void>(
context: outerContext,
builder: (context) {
return AlertDialog(
title: Text(feedback.text),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (feedback.extra != null) Text(feedback.extra!.toString()),
Image.memory(
feedback.screenshot,
height: 600,
width: 500,
fit: BoxFit.contain,
),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Close'),
onPressed: () {
Navigator.pop(context);
},
)
],
);
},
);
}

class SettingsRoute extends StatelessWidget {
TextStyle get headingStyle => const TextStyle(
fontSize: 16,
Expand All @@ -21,7 +60,7 @@ class SettingsRoute extends StatelessWidget {
const SettingsRoute({super.key});

@override
Widget build(BuildContext context) {
Widget build(BuildContext context_) {
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: BlocBuilder<SettingsBloc, SettingsState>(
Expand Down Expand Up @@ -67,6 +106,22 @@ class SettingsRoute extends StatelessWidget {
title: const Text('Plugins'),
builder: (context) => const PluginListRoute(),
),
SettingsTileSwitch(
title: Text("FeedBack"),
value: false,
onChanged: (value) {
BetterFeedback.of(context_).show(
(feedback) async {
// upload to server, share whatever
// for example purposes just show it to the user
alertFeedbackFunction(
context,
feedback,
);
},
);
},
)
],
),
// SettingsSection(
Expand Down
21 changes: 21 additions & 0 deletions app/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
feedback:
dependency: "direct main"
description:
name: feedback
sha256: "26769f73de6215add72074d24e4a23542e4c02a8fd1a873e7c93da5dc9c1d362"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
ffi:
dependency: transitive
description:
Expand Down Expand Up @@ -307,6 +315,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_native_splash:
dependency: "direct dev"
description:
Expand Down Expand Up @@ -447,6 +460,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
intl:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
io:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies:
url_launcher: ^6.3.1
intersperse: ^2.0.0
http: ^1.4.0
feedback: ^3.1.0

dev_dependencies:
flutter_test:
Expand Down
Loading