feat(ocr): 🚧 work on adding entries through OCR
This commit is contained in:
parent
de6c5fe315
commit
de8f57fcc8
12 changed files with 266 additions and 100 deletions
|
@ -1,5 +1,5 @@
|
||||||
# prasule
|
# prasule
|
||||||
[![Codemagic build status](https://api.codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/status_badge.svg)](https://codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/latest_build) [![Bug issue count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.mnau.xyz%2Fapi%2Fv1%2Frepos%2Fhernik%2Fprasule%2Fissues%3Flabels%3DKind%2FBug&query=%24.length&logo=forgejo&label=bug%20issues&color=red)](https://git.mnau.xyz/hernik/prasule/issues?q=&type=all&sort=&state=open&labels=144&milestone=0&project=0&assignee=0&poster=0)
|
[![Codemagic build status](https://api.codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/status_badge.svg)](https://codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/latest_build) [![Bug issue count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.mnau.xyz%2Fapi%2Fv1%2Frepos%2Fhernik%2Fprasule%2Fissues%3Flabels%3DKind%2FBug&query=%24.length&logo=forgejo&label=bug%20issues&color=red)](https://git.mnau.xyz/hernik/prasule/issues?q=&type=all&sort=&state=open&labels=144&milestone=0&project=0&assignee=0&poster=0) [![wakatime](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c.svg)](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c)
|
||||||
|
|
||||||
Expense manager
|
Expense manager
|
||||||
|
|
||||||
|
|
15
lib/api/entry_data.dart
Normal file
15
lib/api/entry_data.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
part 'entry_data.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class EntryData {
|
||||||
|
String name;
|
||||||
|
double amount;
|
||||||
|
|
||||||
|
EntryData({required this.name, required this.amount});
|
||||||
|
|
||||||
|
factory EntryData.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$EntryDataFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$EntryDataToJson(this);
|
||||||
|
}
|
17
lib/api/entry_data.g.dart
Normal file
17
lib/api/entry_data.g.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'entry_data.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
EntryData _$EntryDataFromJson(Map<String, dynamic> json) => EntryData(
|
||||||
|
name: json['name'] as String,
|
||||||
|
amount: (json['amount'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$EntryDataToJson(EntryData instance) => <String, dynamic>{
|
||||||
|
'name': instance.name,
|
||||||
|
'amount': instance.amount,
|
||||||
|
};
|
25
lib/api/multientry.dart
Normal file
25
lib/api/multientry.dart
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:prasule/api/category.dart';
|
||||||
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
part 'multientry.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class MultiEntry {
|
||||||
|
EntryType type;
|
||||||
|
List<EntryData> data;
|
||||||
|
DateTime date;
|
||||||
|
WalletCategory category;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
MultiEntry(
|
||||||
|
{required this.data,
|
||||||
|
required this.type,
|
||||||
|
required this.date,
|
||||||
|
required this.category,
|
||||||
|
required this.id});
|
||||||
|
|
||||||
|
factory MultiEntry.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$MultiEntryFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$MultiEntryToJson(this);
|
||||||
|
}
|
32
lib/api/multientry.g.dart
Normal file
32
lib/api/multientry.g.dart
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'multientry.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
MultiEntry _$MultiEntryFromJson(Map<String, dynamic> json) => MultiEntry(
|
||||||
|
data: (json['data'] as List<dynamic>)
|
||||||
|
.map((e) => EntryData.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
type: $enumDecode(_$EntryTypeEnumMap, json['type']),
|
||||||
|
date: DateTime.parse(json['date'] as String),
|
||||||
|
category:
|
||||||
|
WalletCategory.fromJson(json['category'] as Map<String, dynamic>),
|
||||||
|
id: json['id'] as int,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$MultiEntryToJson(MultiEntry instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'type': _$EntryTypeEnumMap[instance.type]!,
|
||||||
|
'data': instance.data,
|
||||||
|
'date': instance.date.toIso8601String(),
|
||||||
|
'category': instance.category,
|
||||||
|
'id': instance.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$EntryTypeEnumMap = {
|
||||||
|
EntryType.expense: 'expense',
|
||||||
|
EntryType.income: 'income',
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:currency_picker/currency_picker.dart';
|
import 'package:currency_picker/currency_picker.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
import 'package:prasule/api/entry.dart';
|
import 'package:prasule/api/walletentry.dart';
|
||||||
part 'wallet.g.dart';
|
part 'wallet.g.dart';
|
||||||
|
|
||||||
Currency _currencyFromJson(Map<String, dynamic> data) =>
|
Currency _currencyFromJson(Map<String, dynamic> data) =>
|
||||||
|
@ -11,7 +11,7 @@ Currency _currencyFromJson(Map<String, dynamic> data) =>
|
||||||
class Wallet {
|
class Wallet {
|
||||||
final String name;
|
final String name;
|
||||||
final List<WalletCategory> categories;
|
final List<WalletCategory> categories;
|
||||||
final List<WalletEntry> entries;
|
final List<WalletSingleEntry> entries;
|
||||||
double availableAmount;
|
double availableAmount;
|
||||||
@JsonKey(fromJson: _currencyFromJson)
|
@JsonKey(fromJson: _currencyFromJson)
|
||||||
final Currency currency;
|
final Currency currency;
|
||||||
|
|
|
@ -14,7 +14,8 @@ Wallet _$WalletFromJson(Map<String, dynamic> json) => Wallet(
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
entries: (json['entries'] as List<dynamic>?)
|
entries: (json['entries'] as List<dynamic>?)
|
||||||
?.map((e) => WalletEntry.fromJson(e as Map<String, dynamic>))
|
?.map(
|
||||||
|
(e) => WalletSingleEntry.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
availableAmount: (json['availableAmount'] as num?)?.toDouble() ?? 0,
|
availableAmount: (json['availableAmount'] as num?)?.toDouble() ?? 0,
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
part 'entry.g.dart';
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
part 'walletentry.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class WalletEntry {
|
|
||||||
|
/// This is an entry containing a single item
|
||||||
|
class WalletSingleEntry {
|
||||||
EntryType type;
|
EntryType type;
|
||||||
String name;
|
EntryData data;
|
||||||
double amount;
|
|
||||||
DateTime date;
|
DateTime date;
|
||||||
WalletCategory category;
|
WalletCategory category;
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
WalletEntry(
|
WalletSingleEntry(
|
||||||
{required this.name,
|
{required this.data,
|
||||||
required this.amount,
|
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.category,
|
required this.category,
|
||||||
|
@ -21,9 +22,9 @@ class WalletEntry {
|
||||||
|
|
||||||
/// Connect the generated [_$WalletEntry] function to the `fromJson`
|
/// Connect the generated [_$WalletEntry] function to the `fromJson`
|
||||||
/// factory.
|
/// factory.
|
||||||
factory WalletEntry.fromJson(Map<String, dynamic> json) =>
|
factory WalletSingleEntry.fromJson(Map<String, dynamic> json) =>
|
||||||
_$WalletEntryFromJson(json);
|
_$WalletSingleEntryFromJson(json);
|
||||||
|
|
||||||
/// Connect the generated [_$WalletEntryToJson] function to the `toJson` method.
|
/// Connect the generated [_$WalletEntryToJson] function to the `toJson` method.
|
||||||
Map<String, dynamic> toJson() => _$WalletEntryToJson(this);
|
Map<String, dynamic> toJson() => _$WalletSingleEntryToJson(this);
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'entry.dart';
|
part of 'walletentry.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
WalletEntry _$WalletEntryFromJson(Map<String, dynamic> json) => WalletEntry(
|
WalletSingleEntry _$WalletSingleEntryFromJson(Map<String, dynamic> json) =>
|
||||||
name: json['name'] as String,
|
WalletSingleEntry(
|
||||||
amount: (json['amount'] as num).toDouble(),
|
data: EntryData.fromJson(json['data'] as Map<String, dynamic>),
|
||||||
type: $enumDecode(_$EntryTypeEnumMap, json['type']),
|
type: $enumDecode(_$EntryTypeEnumMap, json['type']),
|
||||||
date: DateTime.parse(json['date'] as String),
|
date: DateTime.parse(json['date'] as String),
|
||||||
category:
|
category:
|
||||||
|
@ -16,11 +16,10 @@ WalletEntry _$WalletEntryFromJson(Map<String, dynamic> json) => WalletEntry(
|
||||||
id: json['id'] as int,
|
id: json['id'] as int,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$WalletEntryToJson(WalletEntry instance) =>
|
Map<String, dynamic> _$WalletSingleEntryToJson(WalletSingleEntry instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'type': _$EntryTypeEnumMap[instance.type]!,
|
'type': _$EntryTypeEnumMap[instance.type]!,
|
||||||
'name': instance.name,
|
'data': instance.data,
|
||||||
'amount': instance.amount,
|
|
||||||
'date': instance.date.toIso8601String(),
|
'date': instance.date.toIso8601String(),
|
||||||
'category': instance.category,
|
'category': instance.category,
|
||||||
'id': instance.id,
|
'id': instance.id,
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
import 'package:prasule/api/entry.dart';
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
import 'package:prasule/api/walletentry.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/walletmanager.dart';
|
import 'package:prasule/api/walletmanager.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
|
@ -9,7 +10,7 @@ import 'package:prasule/pw/platformfield.dart';
|
||||||
|
|
||||||
class CreateEntryView extends StatefulWidget {
|
class CreateEntryView extends StatefulWidget {
|
||||||
final Wallet w;
|
final Wallet w;
|
||||||
final WalletEntry? editEntry;
|
final WalletSingleEntry? editEntry;
|
||||||
const CreateEntryView({super.key, required this.w, this.editEntry});
|
const CreateEntryView({super.key, required this.w, this.editEntry});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -17,7 +18,7 @@ class CreateEntryView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CreateEntryViewState extends State<CreateEntryView> {
|
class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
late WalletEntry newEntry;
|
late WalletSingleEntry newEntry;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -28,10 +29,9 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
while (widget.w.entries.where((element) => element.id == id).isNotEmpty) {
|
while (widget.w.entries.where((element) => element.id == id).isNotEmpty) {
|
||||||
id++; // create unique ID
|
id++; // create unique ID
|
||||||
}
|
}
|
||||||
newEntry = WalletEntry(
|
newEntry = WalletSingleEntry(
|
||||||
id: id,
|
id: id,
|
||||||
name: "",
|
data: EntryData(amount: 0, name: ""),
|
||||||
amount: 0,
|
|
||||||
type: EntryType.expense,
|
type: EntryType.expense,
|
||||||
date: DateTime.now(),
|
date: DateTime.now(),
|
||||||
category: widget.w.categories.first);
|
category: widget.w.categories.first);
|
||||||
|
@ -57,9 +57,9 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
width: MediaQuery.of(context).size.width * 0.8,
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
child: PlatformField(
|
child: PlatformField(
|
||||||
labelText: "Name",
|
labelText: "Name",
|
||||||
controller: TextEditingController(text: newEntry.name),
|
controller: TextEditingController(text: newEntry.data.name),
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
newEntry.name = v;
|
newEntry.data.name = v;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -70,8 +70,8 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
width: MediaQuery.of(context).size.width * 0.8,
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
child: PlatformField(
|
child: PlatformField(
|
||||||
labelText: "Amount",
|
labelText: "Amount",
|
||||||
controller:
|
controller: TextEditingController(
|
||||||
TextEditingController(text: newEntry.amount.toString()),
|
text: newEntry.data.amount.toString()),
|
||||||
keyboardType:
|
keyboardType:
|
||||||
const TextInputType.numberWithOptions(decimal: true),
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
|
@ -79,7 +79,7 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
RegExp(r'\d+[\.,]{0,1}\d{0,}'))
|
RegExp(r'\d+[\.,]{0,1}\d{0,}'))
|
||||||
],
|
],
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
newEntry.amount = double.parse(v);
|
newEntry.data.amount = double.parse(v);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -157,7 +157,7 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: "Save",
|
text: "Save",
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (newEntry.name.isEmpty) {
|
if (newEntry.data.name.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).clearSnackBars();
|
ScaffoldMessenger.of(context).clearSnackBars();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
|
|
|
@ -6,7 +6,8 @@ import 'package:grouped_list/grouped_list.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:prasule/api/entry.dart';
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
import 'package:prasule/api/walletentry.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/walletmanager.dart';
|
import 'package:prasule/api/walletmanager.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
|
@ -14,6 +15,7 @@ import 'package:prasule/network/tessdata.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
import 'package:prasule/pw/platformdialog.dart';
|
||||||
import 'package:prasule/views/create_entry.dart';
|
import 'package:prasule/views/create_entry.dart';
|
||||||
|
import 'package:prasule/views/multientry_creator.dart';
|
||||||
import 'package:prasule/views/settings/settings.dart';
|
import 'package:prasule/views/settings/settings.dart';
|
||||||
import 'package:prasule/views/settings/tessdata_list.dart';
|
import 'package:prasule/views/settings/tessdata_list.dart';
|
||||||
import 'package:prasule/views/setup.dart';
|
import 'package:prasule/views/setup.dart';
|
||||||
|
@ -162,7 +164,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
SlidableAction(
|
SlidableAction(
|
||||||
onPressed: (c) {
|
onPressed: (c) {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.push<WalletEntry>(
|
.push<WalletSingleEntry>(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (c) => CreateEntryView(
|
builder: (c) => CreateEntryView(
|
||||||
w: selectedWallet!,
|
w: selectedWallet!,
|
||||||
|
@ -237,9 +239,9 @@ class _HomeViewState extends State<HomeView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(element.name),
|
title: Text(element.data.name),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
"${element.amount} ${selectedWallet!.currency.symbol}"),
|
"${element.data.amount} ${selectedWallet!.currency.symbol}"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -272,12 +274,12 @@ class _HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
var selectedLanguages = List<bool>.filled(availableLanguages.length, false);
|
var selectedLanguages = List<bool>.filled(availableLanguages.length, false);
|
||||||
if (selectedLanguages.length == 1) {
|
|
||||||
selectedLanguages[0] = true;
|
selectedLanguages[0] = true;
|
||||||
}
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => StatefulBuilder(
|
||||||
|
builder: (ctx, setState) => PlatformDialog(
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -289,20 +291,46 @@ class _HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
// get selected languages
|
// get selected languages
|
||||||
var selected = availableLanguages
|
var selected = availableLanguages
|
||||||
.where((element) =>
|
.where((element) => selectedLanguages[
|
||||||
selectedLanguages[availableLanguages.indexOf(element)])
|
availableLanguages.indexOf(element)])
|
||||||
.join("+")
|
.join("+")
|
||||||
.replaceAll(".traineddata", "");
|
.replaceAll(".traineddata", "");
|
||||||
logger.i(selected);
|
logger.i(selected);
|
||||||
|
if (!mounted) return;
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (c) => const PlatformDialog(
|
||||||
|
title:
|
||||||
|
"Loading text from image, please wait a moment..."),
|
||||||
|
barrierDismissible: false);
|
||||||
var string = await FlutterTesseractOcr.extractText(media.path,
|
var string = await FlutterTesseractOcr.extractText(media.path,
|
||||||
language: selected,
|
language: selected,
|
||||||
args: {
|
args: {
|
||||||
//"psm": "4",
|
"psm": "4",
|
||||||
"preserve_interword_spaces": "1",
|
"preserve_interword_spaces": "1",
|
||||||
});
|
});
|
||||||
|
if (!mounted) return;
|
||||||
|
Navigator.of(context).pop();
|
||||||
logger.i(string);
|
logger.i(string);
|
||||||
if (mounted) Navigator.of(context).pop();
|
if (!mounted) return;
|
||||||
return;
|
var lines = string.split("\n")
|
||||||
|
..removeWhere((element) {
|
||||||
|
element.trim();
|
||||||
|
return element.isEmpty;
|
||||||
|
});
|
||||||
|
|
||||||
|
var data = <EntryData>[];
|
||||||
|
for (var line in lines) {
|
||||||
|
var regex = RegExp(r'');
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (c) => MultientryCreateView(
|
||||||
|
linesToAdd: data,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: const Text("Ok")),
|
child: const Text("Ok")),
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -328,7 +356,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
1 &&
|
1 &&
|
||||||
!value)) return;
|
!value)) return;
|
||||||
selectedLanguages[index] = value;
|
selectedLanguages[index] = value;
|
||||||
setState(() {}); // todo: builder
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -341,6 +369,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +381,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
final List<XFile>? files = response.files;
|
final List<XFile>? files = response.files;
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
|
logger.i("Found lost files");
|
||||||
_handleLostFiles(files);
|
_handleLostFiles(files);
|
||||||
} else {
|
} else {
|
||||||
logger.e(response.exception);
|
logger.e(response.exception);
|
||||||
|
|
46
lib/views/multientry_creator.dart
Normal file
46
lib/views/multientry_creator.dart
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
|
||||||
|
class MultientryCreateView extends StatefulWidget {
|
||||||
|
const MultientryCreateView({super.key, required this.linesToAdd});
|
||||||
|
final List<EntryData> linesToAdd;
|
||||||
|
@override
|
||||||
|
State<MultientryCreateView> createState() => _MultientryCreateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MultientryCreateViewState extends State<MultientryCreateView> {
|
||||||
|
final _isOpen = <bool>[];
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isOpen.clear();
|
||||||
|
_isOpen.addAll(List<bool>.filled(widget.linesToAdd.length, false));
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("Add from OCR"),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: ExpansionPanelList(
|
||||||
|
expansionCallback: (i, expanded) {
|
||||||
|
_isOpen[i] = !_isOpen[i];
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
children: List.generate(
|
||||||
|
widget.linesToAdd.length,
|
||||||
|
(index) => ExpansionPanel(
|
||||||
|
headerBuilder: (c, expanded) => const Text(""),
|
||||||
|
body: Text(
|
||||||
|
widget.linesToAdd[index].name,
|
||||||
|
),
|
||||||
|
isExpanded: _isOpen[index]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue