Skip to content
Merged
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
14 changes: 14 additions & 0 deletions lib/core/models/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ class Task {
return Task.fromIssue(issue, slug);
});

/// Deletes the issue from the remote repository.
Future<void> deleteRemote() async {
if (_issueNumber == null) {
Logger.logWarning(
"Cannot delete issue $_issueNumber, it does not exist",
_classId,
);
return;
}
Logger.logInfo("Deleting issue $_issueNumber", _classId);
await (await GithubModel.github).issues.deleteIssue(_slug, _issueNumber);
Logger.logInfo("Successfully deleted issue $_issueNumber", _classId);
}

/// Creates a copy of the current instance.
Task copy() => Task(
title: title,
Expand Down
18 changes: 16 additions & 2 deletions lib/core/task_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ class TaskHandler extends ChangeNotifier {
return task;
}

/// Updates an existing task in the list of tasks. Notifies listeners
/// about the change.
/// Updates an existing task in the list of tasks. Notifies listeners about the change.
/// Does not save the task to the remote repository!
void updateLocalTask(final Task task) {
final int index = _tasks.indexWhere(
Expand All @@ -116,6 +115,21 @@ class TaskHandler extends ChangeNotifier {
}
}

/// Deletes a task from the list of tasks. Notifies listeners about the change.
/// Also deletes the task from the remote repository.
/// If the task does not exist in the remote repository, it will be removed from the local list.
Future<bool> deleteTask(final Task task) async {
try {
await task.deleteRemote();
_tasks.removeWhere((final t) => t.issueNumber == task.issueNumber);
notifyListeners();
return true;
} on Exception catch (e) {
Logger.logError("Failed to delete task", _classId, e);
}
return false;
}

Future<List<Task>> _fetchIssuesForRepository(
final RepositoryDetails repo,
) async => (await GithubModel.github).issues
Expand Down
14 changes: 14 additions & 0 deletions lib/core/utils/snack_bar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "package:flutter/material.dart";

/// Utility class for showing Snack bars safely.
class SnackBarUtils {
/// Shows a SnackBar with the given [message].
static void show({
required final BuildContext context,
required final String message,
}) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
}
31 changes: 28 additions & 3 deletions lib/ui/_widgets/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import "package:gitdone/ui/_widgets/app_title.dart";
/// A normal app bar that is used in most of the views.
class NormalAppBar extends StatelessWidget implements PreferredSizeWidget {
/// Creates a normal app bar with an optional back button.
const NormalAppBar({super.key, this.backVisible = true});
const NormalAppBar({
super.key,
this.backVisible = true,
this.menuItems = const [],
});

/// Whether the back button should be visible.
final dynamic backVisible;
final bool backVisible;

/// The actions to be displayed in the app bar om PopupMenuButton.
final List<MenuItemButton> menuItems;

@override
Widget build(final BuildContext context) => AppBar(
Expand All @@ -26,7 +33,25 @@ class NormalAppBar extends StatelessWidget implements PreferredSizeWidget {
onPressed: Navigation.navigateBack,
)
: const SizedBox(width: 48),
actions: const [SizedBox(width: 48)],
actions: [
if (menuItems.isNotEmpty) _buildMenu() else const SizedBox(width: 48),
],
);

Widget _buildMenu() => MenuAnchor(
builder: (final context, final controller, final child) => IconButton(
onPressed: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
icon: const Icon(Icons.more_vert),
tooltip: "Show menu",
),
alignmentOffset: const Offset(0, -4), // First item exactly below app bar
menuChildren: menuItems,
);

@override
Expand Down
73 changes: 73 additions & 0 deletions lib/ui/_widgets/confirm_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import "package:flutter/material.dart";
import "package:gitdone/core/utils/navigation.dart";

/// A dialog that asks the user to confirm an action.
class ConfirmDialog extends StatelessWidget {
/// Creates a [ConfirmDialog] widget with the given parameters.
const ConfirmDialog({
required final Widget title,
required final Widget content,
required final String confirmText,
required final VoidCallback onConfirm,
required final String cancelText,
final VoidCallback? onCancel,
super.key,
}) : _title = title,
_content = content,
_confirmText = confirmText,
_cancelText = cancelText,
_onConfirm = onConfirm,
_onCancel = onCancel;

final Widget _title;
final Widget _content;
final String _confirmText;
final String _cancelText;
final VoidCallback _onConfirm;
final VoidCallback? _onCancel;

@override
Widget build(final BuildContext context) => AlertDialog(
title: _title,
content: _content,
actions: <Widget>[
TextButton(
onPressed: () {
Navigation.navigateBack();
_onConfirm();
},
child: Text(_confirmText),
),
TextButton(
onPressed: () {
Navigation.navigateBack();
if (_onCancel != null) _onCancel();
},
child: Text(_cancelText),
),
],
);

/// Shows the ConfirmDialog as a dialog.
static Future<void> show(
final BuildContext context, {
required final Widget title,
required final Widget content,
required final String confirmText,
required final VoidCallback onConfirm,
final String cancelText = "Cancel",
final VoidCallback? onCancel,
final bool barrierDismissible = true,
}) => showDialog<void>(
context: context,
barrierDismissible: barrierDismissible,
builder: (final context) => ConfirmDialog(
title: title,
content: content,
confirmText: confirmText,
cancelText: cancelText,
onConfirm: onConfirm,
onCancel: onCancel,
),
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "package:flutter/material.dart";
import "package:gitdone/core/models/repository_details.dart";
import "package:gitdone/core/utils/snack_bar.dart";
import "package:gitdone/ui/settings/widgets/repository_selector/repository_selector_view_model.dart";
import "package:provider/provider.dart";

Expand Down Expand Up @@ -46,9 +47,9 @@ class _RepositorySelectorState extends State<RepositorySelector> {
model
..selectRepository(repo)
..saveSelectedRepository();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Selected repository: ${repo?.name}")),
SnackBarUtils.show(
context: context,
message: "Selected repository: ${repo?.name}",
);
},
),
Expand Down
Loading
Loading