Skip to content

Commit

Permalink
chore: Configure remote path on the setup view
Browse files Browse the repository at this point in the history
  • Loading branch information
tmaegel committed Jun 9, 2024
1 parent 6c5e5eb commit 95712e6
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 135 deletions.
195 changes: 103 additions & 92 deletions lib/presentation/login/pages/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class _LocalLoginViewState extends State<LocalLoginView> {

@override
Widget build(BuildContext context) {
bool keyboardIsOpen = MediaQuery.of(context).viewInsets.bottom != 0;

return Stack(
children: [
Scaffold(
Expand All @@ -41,31 +43,30 @@ class _LocalLoginViewState extends State<LocalLoginView> {
const LocalPathInput(),
],
),
floatingActionButton: BlocBuilder<TodoFileCubit, TodoFileState>(
builder: (BuildContext context, TodoFileState state) {
return Visibility(
visible: state is! TodoFileLoading,
child: FloatingActionButton.extended(
heroTag: 'localUsage',
icon: const Icon(Icons.done),
label: const Text('Apply'),
tooltip: 'Apply',
onPressed: () async {
try {
setState(() => loading = true);
await context.read<LoginCubit>().loginLocal(
localTodoFile: File(
'${state.localPath}${Platform.pathSeparator}${state.todoFilename}',
),
);
} finally {
setState(() => loading = false);
}
floatingActionButton: keyboardIsOpen
? null
: BlocBuilder<TodoFileCubit, TodoFileState>(
builder: (BuildContext context, TodoFileState state) {
return FloatingActionButton.extended(
heroTag: 'localUsage',
icon: const Icon(Icons.done),
label: const Text('Apply'),
tooltip: 'Apply',
onPressed: () async {
try {
setState(() => loading = true);
await context.read<LoginCubit>().loginLocal(
localTodoFile: File(
'${state.localPath}${Platform.pathSeparator}${state.todoFilename}',
),
);
} finally {
setState(() => loading = false);
}
},
);
},
),
);
},
),
),
if (loading)
const Opacity(
Expand Down Expand Up @@ -375,98 +376,104 @@ class RemotePathInput extends StatefulWidget {
}

class _RemotePathInputState extends State<RemotePathInput> {
late TextEditingController controller;
final TextEditingController controller = TextEditingController();
final Debouncer debounce = Debouncer(milliseconds: 1000);

@override
void initState() {
super.initState();
controller = TextEditingController();
}

@override
void dispose() {
controller.dispose();
debounce.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
// Initial value
controller.text = context.read<TodoFileCubit>().state.remotePath;
return BlocBuilder<TodoFileCubit, TodoFileState>(
builder: (BuildContext context, TodoFileState state) {
controller.text = state.remotePath;
return ListTile(
leading: const Icon(Icons.folder),
title: TextFormField(
controller: controller,
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
labelText: 'Remote path',
hintText: defaultRemoteTodoPath,
leading: const Icon(Icons.folder),
title: TextFormField(
controller: controller,
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
labelText: 'Remote path',
hintText: defaultRemoteTodoPath,
),
onChanged: (String value) async {
debounce.run(() async => await _save(context, value));
},
),
onChanged: (String value) =>
context.read<TodoFileCubit>().updateRemotePath(value),
),
trailing: state is! TodoFileLoading
? IconButton(
icon: const Icon(Icons.help_outline),
onPressed: () => InfoDialog.dialog(
context: context,
title: 'Remote path',
message:
'This path is appended to the base url of the server connection. This makes it possible to define a user-defined path for the todo files.',
),
)
: IconButton(
icon: const Icon(Icons.save),
onPressed: () async {
if (controller.text.isEmpty) {
SnackBarHandler.info(
context,
'Empty remote path is not allowed. Using default one.',
);
await context
.read<TodoFileCubit>()
.saveRemotePath(defaultRemoteTodoPath);
} else {
await context
.read<TodoFileCubit>()
.saveRemotePath(controller.text);
}
},
),
);
trailing: IconButton(
icon: const Icon(Icons.help_outline),
onPressed: () => InfoDialog.dialog(
context: context,
title: 'Remote path',
message:
'This path is appended to the base url of the server connection. This makes it possible to define a user-defined path for the todo files.',
),
));
},
);
}

Future<void> _save(BuildContext context, String value) async {
if (value.isEmpty) {
SnackBarHandler.info(
context,
'Empty remote path is not allowed. Using default one.',
);
await context.read<TodoFileCubit>().saveRemotePath(defaultRemoteTodoPath);
controller.value = controller.value.copyWith(
text: defaultRemoteTodoPath,
selection:
const TextSelection.collapsed(offset: defaultRemoteTodoPath.length),
);
} else {
await context.read<TodoFileCubit>().saveRemotePath(value);
controller.value = controller.value.copyWith(
text: value,
selection: TextSelection.collapsed(offset: value.length),
);
}
}
}

class TodoFilenameInput extends StatefulWidget {
const TodoFilenameInput({super.key});

@override
State<TodoFilenameInput> createState() => _LocalFilenameInputState();
State<TodoFilenameInput> createState() => _TodoFilenameInputState();
}

class _LocalFilenameInputState extends State<TodoFilenameInput> {
late TextEditingController controller;
class _TodoFilenameInputState extends State<TodoFilenameInput> {
final TextEditingController controller = TextEditingController();
final Debouncer debounce = Debouncer(milliseconds: 1000);

@override
void initState() {
super.initState();
controller = TextEditingController();
}

@override
void dispose() {
controller.dispose();
debounce.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
// Initial value
controller.text = context.read<TodoFileCubit>().state.todoFilename;
return BlocBuilder<TodoFileCubit, TodoFileState>(
builder: (BuildContext context, TodoFileState state) {
controller.text = state.todoFilename;
return ListTile(
leading: const Icon(Icons.description),
title: TextFormField(
Expand All @@ -476,31 +483,35 @@ class _LocalFilenameInputState extends State<TodoFilenameInput> {
labelText: 'Todo filename',
hintText: defaultTodoFilename,
),
onChanged: (String value) =>
context.read<TodoFileCubit>().updateTodoFilename(value),
onChanged: (String value) async {
debounce.run(() async => await _save(context, value));
},
),
trailing: state is! TodoFileLoading
? null
: IconButton(
icon: const Icon(Icons.save),
onPressed: () async {
if (controller.text.isEmpty) {
SnackBarHandler.info(
context,
'Empty todo filename is not allowed. Using default one.',
);
await context
.read<TodoFileCubit>()
.saveLocalFilename(defaultTodoFilename);
} else {
await context
.read<TodoFileCubit>()
.saveLocalFilename(controller.text);
}
},
),
);
},
);
}

Future<void> _save(BuildContext context, String value) async {
if (value.isEmpty) {
SnackBarHandler.info(
context,
'Empty todo filename is not allowed. Using default one.',
);
await context
.read<TodoFileCubit>()
.saveLocalFilename(defaultTodoFilename);
controller.value = controller.value.copyWith(
text: defaultTodoFilename,
selection:
const TextSelection.collapsed(offset: defaultTodoFilename.length),
);
} else {
await context.read<TodoFileCubit>().saveLocalFilename(value);
controller.value = controller.value.copyWith(
text: value,
selection: TextSelection.collapsed(offset: value.length),
);
}
}
}
2 changes: 1 addition & 1 deletion lib/presentation/todo/widgets/todo_text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class _TodoStringTextFieldState extends State<TodoStringTextField> {
text: state.todo.description,
selection: TextSelection.fromPosition(
TextPosition(
offset: base > state.todo.description.length
offset: base < 0 || base > state.todo.description.length
? state.todo.description.length
: base,
),
Expand Down
66 changes: 28 additions & 38 deletions lib/presentation/todo_file/todo_file_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,14 @@ class TodoFileCubit extends Cubit<TodoFileState> {
}
}

Future<void> updateLocalPath(String? value) async {
if (value != null) {
log.fine('Updating \'localPath\'.');
emit(state.load(localPath: value));
}
}

Future<void> saveLocalPath(String? value) async {
try {
if (value != null) {
emit(state.load());
log.fine('Saving setting \'localPath\'.');
if (!value.endsWith('/')) {
value = '$value/';
}
await repository.updateOrInsert(
Setting(key: 'localPath', value: value),
);
Expand All @@ -121,17 +118,12 @@ class TodoFileCubit extends Cubit<TodoFileState> {
}
}

Future<void> updateTodoFilename(String? value) async {
if (value != null) {
log.fine('Updating \'todoFilename\'.');
emit(state.load(todoFilename: value));
}
}

Future<void> saveLocalFilename(String? value) async {
try {
if (value != null) {
emit(state.load());
log.fine('Saving setting \'todoFilename\'.');

await repository.updateOrInsert(
Setting(key: 'todoFilename', value: value),
);
Expand All @@ -147,17 +139,14 @@ class TodoFileCubit extends Cubit<TodoFileState> {
}
}

Future<void> updateRemotePath(String? value) async {
if (value != null) {
log.fine('Updating \'remotePath\'.');
emit(state.load(remotePath: value));
}
}

Future<void> saveRemotePath(String? value) async {
try {
if (value != null) {
emit(state.load());
log.fine('Saving setting \'remotePath\'.');
if (!value.endsWith('/')) {
value = '$value/';
}
await repository.updateOrInsert(
Setting(key: 'remotePath', value: value),
);
Expand All @@ -168,25 +157,9 @@ class TodoFileCubit extends Cubit<TodoFileState> {
}
}

Future<void> resetTodoFileSettings() async {
try {
log.fine('Resetting todofile settings.');
for (var k in [
'todoFilename',
'localFilename', // @todo: Keep for backwards compatibility.
'localPath',
'remotePath',
]) {
log.fine('Deleting setting \'$k\'.');
await repository.delete(key: k);
}
} on Exception catch (e) {
emit(state.error(message: e.toString()));
}
}

Future<void> resetToDefaults() async {
try {
emit(state.load());
log.fine('Resetting to the defaults.');
await resetTodoFileSettings();
emit(
Expand All @@ -201,4 +174,21 @@ class TodoFileCubit extends Cubit<TodoFileState> {
emit(state.error(message: e.toString()));
}
}

Future<void> resetTodoFileSettings() async {
try {
log.fine('Resetting todofile settings.');
for (var k in [
'todoFilename',
'localFilename', // @todo: Keep for backwards compatibility.
'localPath',
'remotePath',
]) {
log.fine('Deleting setting \'$k\'.');
await repository.delete(key: k);
}
} on Exception catch (e) {
emit(state.error(message: e.toString()));
}
}
}
Loading

0 comments on commit 95712e6

Please sign in to comment.