Skip to content
This repository has been archived by the owner on Sep 14, 2024. It is now read-only.

Commit

Permalink
rework import screen
Browse files Browse the repository at this point in the history
- validate input to be a url
- enable submitting with the keyboard
  • Loading branch information
Leptopoda committed Apr 21, 2023
1 parent f02b8ee commit 5438b40
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 135 deletions.
1 change: 0 additions & 1 deletion assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@
"title": "Import Recipe",
"button": "Import",
"field": "URL to Recipe",
"clipboard": "Paste Clipboard",
"errors": {
"import_failed": "Import Failed {error_msg}"
}
Expand Down
102 changes: 0 additions & 102 deletions lib/src/screens/form/recipe_import_form.dart

This file was deleted.

182 changes: 150 additions & 32 deletions lib/src/screens/recipe_import_screen.dart
Original file line number Diff line number Diff line change
@@ -1,55 +1,173 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe_bloc.dart';
import 'package:nextcloud_cookbook_flutter/src/screens/form/recipe_import_form.dart';
import 'package:nextcloud_cookbook_flutter/src/screens/recipe_screen.dart';
import 'package:nextcloud_cookbook_flutter/src/util/theme_data.dart';
import 'package:nextcloud_cookbook_flutter/src/util/url_validator.dart';

class RecipeImportScreen extends StatelessWidget {
class RecipeImportScreen extends StatefulWidget {
final String importUrl;

const RecipeImportScreen([this.importUrl = '']);

@override
State<RecipeImportScreen> createState() => _RecipeImportScreenState();
}

class _RecipeImportScreenState extends State<RecipeImportScreen> {
late TextEditingController _importUrlController;
final _formKey = GlobalKey<FormState>();

@override
void initState() {
super.initState();

_importUrlController = TextEditingController(text: widget.importUrl);
if (widget.importUrl.isNotEmpty) {
SchedulerBinding.instance
.addPostFrameCallback((_) => import(widget.importUrl));
}
}

@override
void dispose() {
_importUrlController.dispose();
super.dispose();
}

void import(String? url) {
if (url != null) {
BlocProvider.of<RecipeBloc>(context).add(RecipeImported(url));
}
}

void onSubmit() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
}
}

String? validate(String? value) {
if (value == null || value.isEmpty) {
return translate(
'login.server_url.validator.pattern',
);
}

if (!URLUtils.isValid(value)) {
return translate(
'login.server_url.validator.pattern',
);
}

return null;
}

Future<void> pasteClipboard() async {
final clipboard = await Clipboard.getData('text/plain');
final text = clipboard?.text;
if (text != null) {
_importUrlController.text = text;
}

_formKey.currentState!.validate();
}

@override
Widget build(BuildContext context) {
return BlocProvider<RecipeBloc>(
create: (context) => RecipeBloc(),
child: Scaffold(
appBar: AppBar(
title: BlocListener<RecipeBloc, RecipeState>(
child: Text(translate("recipe_import.title")),
listener: (context, state) {
if (state.status == RecipeStatus.importFailure) {
final theme = Theme.of(context)
.extension<SnackBarThemes>()!
.errorSnackBar;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
translate(
'recipe_import.errors.import_failed',
args: {"error_msg": state.error},
),
style: theme.contentTextStyle,
),
backgroundColor: theme.backgroundColor,
),
);
} else if (state.status == RecipeStatus.importSuccess) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return RecipeScreen(recipeId: state.recipe!.id!);
},
title: Text(translate("recipe_import.title")),
),
body: BlocConsumer<RecipeBloc, RecipeState>(
builder: builder,
listener: listener,
),
),
);
}

void listener(BuildContext context, RecipeState state) {
if (state.status == RecipeStatus.importFailure) {
final theme =
Theme.of(context).extension<SnackBarThemes>()!.errorSnackBar;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
translate(
'recipe_import.errors.import_failed',
args: {"error_msg": state.error},
),
style: theme.contentTextStyle,
),
backgroundColor: theme.backgroundColor,
),
);
} else if (state.status == RecipeStatus.importSuccess) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return RecipeScreen(recipeId: state.recipe!.id!);
},
),
);
}
}

Widget builder(BuildContext context, RecipeState state) {
final enabled = state.status != RecipeStatus.updateInProgress;

return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
enabled: enabled,
controller: _importUrlController,
validator: enabled ? validate : null,
onSaved: import,
onEditingComplete: onSubmit,
decoration: InputDecoration(
hintText: translate("recipe_import.field"),
suffixIcon: IconButton(
tooltip: MaterialLocalizations.of(context).pasteButtonLabel,
onPressed: pasteClipboard,
icon: const Icon(Icons.content_copy_outlined),
),
);
}
},
),
),
const SizedBox(height: 10),
Center(
child: enabled
? OutlinedButton.icon(
onPressed: enabled ? onSubmit : null,
icon: const Icon(Icons.cloud_download_outlined),
label: Text(translate("recipe_import.button")),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 25),
side: BorderSide(
color: Theme.of(context).colorScheme.onSurface,
),
),
)
: SpinKitWave(
color: Theme.of(context).colorScheme.primary,
size: 30.0,
),
),
],
),
),
body: RecipeImportForm(importUrl),
),
);
}
Expand Down

0 comments on commit 5438b40

Please sign in to comment.