mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
UserProfile
This commit is contained in:
parent
b18daf3acf
commit
b34a91b865
@ -227,10 +227,8 @@ class InvenTreeAPI {
|
|||||||
errorMessage = "Server timeout";
|
errorMessage = "Server timeout";
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
// Unknown error type
|
// Unknown error type - re-throw the error and Sentry will catch it
|
||||||
errorMessage = error.toString();
|
throw error;
|
||||||
// Unknown error type, re-throw error
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import 'package:InvenTree/widget/dialogs.dart';
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
@ -108,7 +111,7 @@ class InvenTreeModel {
|
|||||||
Future<bool> reload(BuildContext context, {bool dialog = false}) async {
|
Future<bool> reload(BuildContext context, {bool dialog = false}) async {
|
||||||
|
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
showProgressDialog(context, "Refreshing data", "Refreshing data for ${NAME}");
|
showProgressDialog(context, I18N.of(context).refreshing, "Refreshing data for ${NAME}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await api.get(url, params: defaultGetFilters())
|
var response = await api.get(url, params: defaultGetFilters())
|
||||||
@ -120,9 +123,10 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e is TimeoutException) {
|
if (e is TimeoutException) {
|
||||||
showErrorDialog(context, "Timeout", "No response from server");
|
showTimeoutDialog(context);
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog(context, "Error", e.toString());
|
// Re-throw the error (Sentry will catch)
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -137,6 +141,12 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
|
showErrorDialog(
|
||||||
|
context,
|
||||||
|
I18N.of(context).serverError,
|
||||||
|
"${I18N.of(context).statusCode}: ${response.statusCode}"
|
||||||
|
);
|
||||||
|
|
||||||
print("Error retrieving data");
|
print("Error retrieving data");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -170,9 +180,10 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e is TimeoutException) {
|
if (e is TimeoutException) {
|
||||||
showErrorDialog(context, "Timeout", "No response from server");
|
showTimeoutDialog(context);
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog(context, "Error", e.toString());
|
// Re-throw the error, let Sentry report it
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -185,7 +196,7 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
print("Error updating ${NAME}: Status code ${response.statusCode}");
|
showErrorDialog(context, I18N.of(context).serverError, "${I18N.of(context).statusCode}: ${response.statusCode}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +228,7 @@ class InvenTreeModel {
|
|||||||
print("GET: $addr ${params.toString()}");
|
print("GET: $addr ${params.toString()}");
|
||||||
|
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
showProgressDialog(context, "Requesting Data", "Requesting ${NAME} data from server");
|
showProgressDialog(context, I18N.of(context).requestingData, "Requesting ${NAME} data from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await api.get(addr, params: params)
|
var response = await api.get(addr, params: params)
|
||||||
@ -229,9 +240,10 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e is TimeoutException) {
|
if (e is TimeoutException) {
|
||||||
showErrorDialog(context, "Timeout", "No response from server");
|
showTimeoutDialog(context);
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog(context, "Error", e.toString());
|
// Re-throw the error (handled by Sentry)
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -243,7 +255,7 @@ class InvenTreeModel {
|
|||||||
hideProgressDialog(context);
|
hideProgressDialog(context);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
print("Error retrieving data");
|
showErrorDialog(context, I18N.of(context).serverError, "${I18N.of(context).statusCode}: ${response.statusCode}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,8 +281,12 @@ class InvenTreeModel {
|
|||||||
await api.post(URL, body: data)
|
await api.post(URL, body: data)
|
||||||
.timeout(Duration(seconds: 5))
|
.timeout(Duration(seconds: 5))
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
print("Error creating new ${NAME}:");
|
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
|
showErrorDialog(
|
||||||
|
context,
|
||||||
|
I18N.of(context).serverError,
|
||||||
|
e.toString()
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.then((http.Response response) {
|
.then((http.Response response) {
|
||||||
@ -279,8 +295,11 @@ class InvenTreeModel {
|
|||||||
var decoded = json.decode(response.body);
|
var decoded = json.decode(response.body);
|
||||||
_model = createFromJson(decoded);
|
_model = createFromJson(decoded);
|
||||||
} else {
|
} else {
|
||||||
print("Error creating object: Status Code ${response.statusCode}");
|
showErrorDialog(
|
||||||
print(response.body);
|
context,
|
||||||
|
I18N.of(context).serverError,
|
||||||
|
"${I18N.of(context).statusCode}: ${response.statusCode}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -308,7 +327,7 @@ class InvenTreeModel {
|
|||||||
// TODO - Add error catching
|
// TODO - Add error catching
|
||||||
|
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
showProgressDialog(context, "Requesting Data", "Requesting ${NAME} data from server");
|
showProgressDialog(context, I18N.of(context).requestingData, "Requesting ${NAME} data from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await api.get(URL, params:params)
|
var response = await api.get(URL, params:params)
|
||||||
@ -320,7 +339,7 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e is TimeoutException) {
|
if (e is TimeoutException) {
|
||||||
showErrorDialog(context, "Timeout", "No response from server");
|
showTimeoutDialog(context);
|
||||||
} else {
|
} else {
|
||||||
// Re-throw the error
|
// Re-throw the error
|
||||||
throw e;
|
throw e;
|
||||||
|
2
lib/l10n
2
lib/l10n
@ -1 +1 @@
|
|||||||
Subproject commit 58e2c5027b481a3a620c27b90ae20b888a02bd96
|
Subproject commit 86fbf66aea18c43cc8920eddce22df5966a7a875
|
@ -1,8 +1,57 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:sembast/sembast.dart';
|
||||||
|
import 'package:sembast/sembast_io.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for storing InvenTree preferences in a NoSql DB
|
||||||
|
*/
|
||||||
|
class InvenTreePreferencesDB {
|
||||||
|
|
||||||
|
static final InvenTreePreferencesDB _singleton = InvenTreePreferencesDB._();
|
||||||
|
|
||||||
|
static InvenTreePreferencesDB get instance => _singleton;
|
||||||
|
|
||||||
|
InvenTreePreferencesDB._();
|
||||||
|
|
||||||
|
Completer<Database> _dbOpenCompleter;
|
||||||
|
|
||||||
|
Future<Database> get database async {
|
||||||
|
// If completer is null, AppDatabaseClass is newly instantiated, so database is not yet opened
|
||||||
|
if (_dbOpenCompleter == null) {
|
||||||
|
_dbOpenCompleter = Completer();
|
||||||
|
// Calling _openDatabase will also complete the completer with database instance
|
||||||
|
_openDatabase();
|
||||||
|
}
|
||||||
|
// If the database is already opened, awaiting the future will happen instantly.
|
||||||
|
// Otherwise, awaiting the returned future will take some time - until complete() is called
|
||||||
|
// on the Completer in _openDatabase() below.
|
||||||
|
return _dbOpenCompleter.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _openDatabase() async {
|
||||||
|
// Get a platform-specific directory where persistent app data can be stored
|
||||||
|
final appDocumentDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
print("Documents Dir: ${appDocumentDir.toString()}");
|
||||||
|
|
||||||
|
print("Path: ${appDocumentDir.path}");
|
||||||
|
|
||||||
|
// Path with the form: /platform-specific-directory/demo.db
|
||||||
|
final dbPath = join(appDocumentDir.path, 'InvenTreeSettings.db');
|
||||||
|
|
||||||
|
final database = await databaseFactoryIo.openDatabase(dbPath);
|
||||||
|
|
||||||
|
// Any code awaiting the Completer's future will now start executing
|
||||||
|
_dbOpenCompleter.complete(database);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class InvenTreePreferences {
|
class InvenTreePreferences {
|
||||||
|
|
||||||
static const String _SERVER = 'server';
|
static const String _SERVER = 'server';
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
|
import 'package:InvenTree/widget/dialogs.dart';
|
||||||
|
import 'package:InvenTree/widget/fields.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
import '../api.dart';
|
import '../api.dart';
|
||||||
import '../preferences.dart';
|
import '../preferences.dart';
|
||||||
|
import '../user_profile.dart';
|
||||||
|
|
||||||
class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
||||||
|
|
||||||
final SharedPreferences _preferences;
|
final SharedPreferences _preferences;
|
||||||
|
|
||||||
InvenTreeLoginSettingsWidget(this._preferences) : super();
|
final List<UserProfile> _profiles;
|
||||||
|
|
||||||
|
InvenTreeLoginSettingsWidget(this._profiles, this._preferences) : super();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_InvenTreeLoginSettingsState createState() => _InvenTreeLoginSettingsState(_preferences);
|
_InvenTreeLoginSettingsState createState() => _InvenTreeLoginSettingsState(_profiles, _preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -20,18 +29,61 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
|
|
||||||
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
||||||
|
|
||||||
|
final _addProfileKey = new GlobalKey<FormState>();
|
||||||
|
|
||||||
final SharedPreferences _preferences;
|
final SharedPreferences _preferences;
|
||||||
|
|
||||||
|
List<UserProfile> profiles;
|
||||||
|
|
||||||
String _server = '';
|
String _server = '';
|
||||||
String _username = '';
|
String _username = '';
|
||||||
String _password = '';
|
String _password = '';
|
||||||
|
|
||||||
_InvenTreeLoginSettingsState(this._preferences) : super() {
|
_InvenTreeLoginSettingsState(this.profiles, this._preferences) : super() {
|
||||||
_server = _preferences.getString('server') ?? '';
|
_server = _preferences.getString('server') ?? '';
|
||||||
_username = _preferences.getString('username') ?? '';
|
_username = _preferences.getString('username') ?? '';
|
||||||
_password = _preferences.getString('password') ?? '';
|
_password = _preferences.getString('password') ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _createProfile(BuildContext context) {
|
||||||
|
|
||||||
|
showFormDialog(
|
||||||
|
context,
|
||||||
|
I18N.of(context).profileAdd,
|
||||||
|
key: _addProfileKey,
|
||||||
|
actions: <Widget> [
|
||||||
|
FlatButton(
|
||||||
|
child: Text(I18N.of(context).cancel),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
child: Text(I18N.of(context).save),
|
||||||
|
onPressed: () {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
fields: <Widget> [
|
||||||
|
StringField(
|
||||||
|
label: I18N.of(context).name,
|
||||||
|
initial: "profile",
|
||||||
|
),
|
||||||
|
StringField(
|
||||||
|
label: "Server",
|
||||||
|
initial: "http://127.0.0.1:8000",
|
||||||
|
),
|
||||||
|
StringField(
|
||||||
|
label: "Username",
|
||||||
|
),
|
||||||
|
StringField(
|
||||||
|
label: "Password"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String _validateServer(String value) {
|
String _validateServer(String value) {
|
||||||
|
|
||||||
@ -71,11 +123,95 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _deleteProfile(UserProfile profile) async {
|
||||||
|
|
||||||
|
await UserProfileDBManager().deleteProfile(profile);
|
||||||
|
|
||||||
|
// Reload profiles
|
||||||
|
profiles = await UserProfileDBManager().getAllProfiles();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
final Size screenSize = MediaQuery.of(context).size;
|
final Size screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
List<Widget> children = [];
|
||||||
|
|
||||||
|
for (int idx = 0; idx < profiles.length; idx++) {
|
||||||
|
|
||||||
|
UserProfile profile = profiles[idx];
|
||||||
|
|
||||||
|
children.add(ListTile(
|
||||||
|
title: Text(profile.name),
|
||||||
|
subtitle: Text(profile.server),
|
||||||
|
trailing: FaIcon(FontAwesomeIcons.checkCircle),
|
||||||
|
onLongPress: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: Text(profile.name),
|
||||||
|
children: <Widget> [
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
// TODO - Mark profile as selected
|
||||||
|
},
|
||||||
|
child: Text(I18N.of(context).profileSelect),
|
||||||
|
),
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () {
|
||||||
|
//Navigator.of(context).pop();
|
||||||
|
// TODO - Edit profile!
|
||||||
|
},
|
||||||
|
child: Text(I18N.of(context).profileEdit),
|
||||||
|
),
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () {
|
||||||
|
// Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
confirmationDialog(
|
||||||
|
context,
|
||||||
|
"Delete",
|
||||||
|
"Delete this profile?",
|
||||||
|
onAccept: () {
|
||||||
|
_deleteProfile(profile);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(I18N.of(context).profileDelete),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(I18N.of(context).profile),
|
||||||
|
),
|
||||||
|
body: Container(
|
||||||
|
child: ListView(
|
||||||
|
children: children,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: Icon(FontAwesomeIcons.plus),
|
||||||
|
onPressed: () {
|
||||||
|
_createProfile(context);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Login Settings"),
|
title: Text("Login Settings"),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:InvenTree/settings/about.dart';
|
import 'package:InvenTree/settings/about.dart';
|
||||||
import 'package:InvenTree/settings/login.dart';
|
import 'package:InvenTree/settings/login.dart';
|
||||||
import 'package:InvenTree/settings/release.dart';
|
import 'package:InvenTree/settings/release.dart';
|
||||||
|
import 'package:InvenTree/user_profile.dart';
|
||||||
|
import 'package:InvenTree/preferences.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -58,6 +60,25 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
|||||||
leading: FaIcon(FontAwesomeIcons.bug),
|
leading: FaIcon(FontAwesomeIcons.bug),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Throw Error"),
|
||||||
|
onTap: () {
|
||||||
|
throw("My custom error");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("add profile"),
|
||||||
|
onTap: () {
|
||||||
|
UserProfileDBManager().addProfile(
|
||||||
|
UserProfile(
|
||||||
|
name: "My Profile",
|
||||||
|
server: "https://127.0.0.1:8000",
|
||||||
|
username: "Oliver",
|
||||||
|
password: "hunter2",
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -68,7 +89,9 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
|||||||
|
|
||||||
var prefs = await SharedPreferences.getInstance();
|
var prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeLoginSettingsWidget(prefs)));
|
List<UserProfile> profiles = await UserProfileDBManager().getAllProfiles();
|
||||||
|
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeLoginSettingsWidget(profiles, prefs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _about() async {
|
void _about() async {
|
||||||
|
109
lib/user_profile.dart
Normal file
109
lib/user_profile.dart
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Class for InvenTree user / login details
|
||||||
|
*/
|
||||||
|
import 'package:sembast/sembast.dart';
|
||||||
|
import 'preferences.dart';
|
||||||
|
|
||||||
|
class UserProfile {
|
||||||
|
|
||||||
|
UserProfile({
|
||||||
|
this.name,
|
||||||
|
this.server,
|
||||||
|
this.username,
|
||||||
|
this.password
|
||||||
|
});
|
||||||
|
|
||||||
|
// Name of the user profile
|
||||||
|
String name;
|
||||||
|
|
||||||
|
// Base address of the InvenTree server
|
||||||
|
String server;
|
||||||
|
|
||||||
|
// Username
|
||||||
|
String username;
|
||||||
|
|
||||||
|
// Password
|
||||||
|
String password;
|
||||||
|
|
||||||
|
// User ID (will be provided by the server on log-in)
|
||||||
|
int user_id;
|
||||||
|
|
||||||
|
factory UserProfile.fromJson(Map<String, dynamic> json) => UserProfile(
|
||||||
|
name: json['name'],
|
||||||
|
server: json['server'],
|
||||||
|
username: json['username'],
|
||||||
|
password: json['password'],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"name": name,
|
||||||
|
"server": server,
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "${server} - ${username}:${password}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserProfileDBManager {
|
||||||
|
|
||||||
|
static const String folder_name = "profiles";
|
||||||
|
|
||||||
|
final _folder = intMapStoreFactory.store(folder_name);
|
||||||
|
|
||||||
|
Future<Database> get _db async => await InvenTreePreferencesDB.instance.database;
|
||||||
|
|
||||||
|
Future addProfile(UserProfile profile) async {
|
||||||
|
|
||||||
|
UserProfile existingProfile = await getProfile(profile.name);
|
||||||
|
|
||||||
|
if (existingProfile != null) {
|
||||||
|
print("UserProfile '${profile.name}' already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _folder.add(await _db, profile.toJson());
|
||||||
|
|
||||||
|
print("Added user profile '${profile.name}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future deleteProfile(UserProfile profile) async {
|
||||||
|
final finder = Finder(filter: Filter.equals("name", profile.name));
|
||||||
|
await _folder.delete(await _db, finder: finder);
|
||||||
|
|
||||||
|
print("Deleted user profile ${profile.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<UserProfile> getProfile(String name) async {
|
||||||
|
// Lookup profile by name (or return null if does not exist)
|
||||||
|
final finder = Finder(filter: Filter.equals("name", name));
|
||||||
|
|
||||||
|
final profiles = await _folder.find(await _db, finder: finder);
|
||||||
|
|
||||||
|
if (profiles.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first matching profile object
|
||||||
|
return UserProfile.fromJson(profiles[0].value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return all user profile objects
|
||||||
|
*/
|
||||||
|
Future<List<UserProfile>> getAllProfiles() async {
|
||||||
|
final profiles = await _folder.find(await _db);
|
||||||
|
|
||||||
|
List<UserProfile> profileList = new List<UserProfile>();
|
||||||
|
|
||||||
|
for (int idx = 0; idx < profiles.length; idx++) {
|
||||||
|
profileList.add(UserProfile.fromJson(profiles[idx].value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return profileList;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,53 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> confirmationDialog(BuildContext context, String title, String text, {String acceptText, String rejectText, Function onAccept, Function onReject}) async {
|
||||||
|
|
||||||
|
if (acceptText == null || acceptText.isEmpty) {
|
||||||
|
acceptText = I18N.of(context).ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rejectText == null || rejectText.isEmpty) {
|
||||||
|
rejectText = I18N.of(context).cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog dialog = AlertDialog(
|
||||||
|
title: ListTile(
|
||||||
|
title: Text(title),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.questionCircle),
|
||||||
|
),
|
||||||
|
content: Text(text),
|
||||||
|
actions: [
|
||||||
|
FlatButton(
|
||||||
|
child: Text(rejectText),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
if (onReject != null) {
|
||||||
|
onReject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
child: Text(acceptText),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
if (onAccept != null) {
|
||||||
|
onAccept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void showMessage(BuildContext context, String message) {
|
void showMessage(BuildContext context, String message) {
|
||||||
Scaffold.of(context).showSnackBar(SnackBar(
|
Scaffold.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(message),
|
content: Text(message),
|
||||||
@ -64,6 +111,18 @@ Future<void> showErrorDialog(BuildContext context, String title, String descript
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showTimeoutDialog(BuildContext context) {
|
||||||
|
/*
|
||||||
|
Show a server timeout dialog
|
||||||
|
*/
|
||||||
|
|
||||||
|
showErrorDialog(
|
||||||
|
context,
|
||||||
|
I18N.of(context).timeout,
|
||||||
|
I18N.of(context).noResponse
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void showProgressDialog(BuildContext context, String title, String description) {
|
void showProgressDialog(BuildContext context, String title, String description) {
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
|
57
pubspec.lock
57
pubspec.lock
@ -42,7 +42,7 @@ packages:
|
|||||||
name: camera
|
name: camera
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.4+5"
|
version: "0.7.0+2"
|
||||||
camera_platform_interface:
|
camera_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -106,6 +106,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.1.3"
|
||||||
|
device_info:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: device_info
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
device_info_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -152,7 +166,7 @@ packages:
|
|||||||
name: flutter_keyboard_visibility
|
name: flutter_keyboard_visibility
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.3"
|
||||||
flutter_keyboard_visibility_platform_interface:
|
flutter_keyboard_visibility_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -251,14 +265,14 @@ packages:
|
|||||||
name: image_picker
|
name: image_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.7+21"
|
version: "0.6.7+22"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_platform_interface
|
name: image_picker_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.6"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -300,7 +314,7 @@ packages:
|
|||||||
name: package_info
|
name: package_info
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.3+2"
|
version: "0.4.3+4"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -392,6 +406,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.14"
|
version: "0.0.14"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -399,13 +420,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.25.0"
|
version: "0.25.0"
|
||||||
|
sembast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sembast
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.9"
|
||||||
sentry:
|
sentry:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: sentry
|
name: sentry
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "4.0.4"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -447,7 +475,7 @@ packages:
|
|||||||
name: shared_preferences_windows
|
name: shared_preferences_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.1+3"
|
version: "0.0.2+3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -466,14 +494,14 @@ packages:
|
|||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2+2"
|
version: "1.3.2+3"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.3+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -564,7 +592,7 @@ packages:
|
|||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.5+1"
|
version: "0.1.5+3"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -572,13 +600,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.1+3"
|
version: "0.0.1+3"
|
||||||
usage:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: usage
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.4.2"
|
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -599,7 +620,7 @@ packages:
|
|||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.4"
|
version: "1.7.4+1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -33,15 +33,17 @@ dependencies:
|
|||||||
preferences: ^5.2.0 # Persistent settings storage
|
preferences: ^5.2.0 # Persistent settings storage
|
||||||
qr_code_scanner: ^0.0.13
|
qr_code_scanner: ^0.0.13
|
||||||
package_info: ^0.4.0 # App information introspection
|
package_info: ^0.4.0 # App information introspection
|
||||||
|
device_info: ^1.0.0 # Information about the device
|
||||||
font_awesome_flutter: ^8.8.1 # FontAwesome icon set
|
font_awesome_flutter: ^8.8.1 # FontAwesome icon set
|
||||||
flutter_speed_dial: ^1.2.5 # FAB menu elements
|
flutter_speed_dial: ^1.2.5 # FAB menu elements
|
||||||
sentry: ^3.0.1 # Error reporting
|
sentry: ^4.0.4 # Error reporting
|
||||||
flutter_typeahead: ^1.8.1 # Auto-complete input field
|
flutter_typeahead: ^1.8.1 # Auto-complete input field
|
||||||
image_picker: ^0.6.6 # Select or take photos
|
image_picker: ^0.6.6 # Select or take photos
|
||||||
url_launcher: ^5.7.10 # Open link in system browser
|
url_launcher: ^5.7.10 # Open link in system browser
|
||||||
flutter_markdown: ^0.5.2 # Rendering markdown
|
flutter_markdown: ^0.5.2 # Rendering markdown
|
||||||
camera:
|
camera:
|
||||||
path_provider:
|
path_provider: ^1.6.27 # Local file storage
|
||||||
|
sembast: ^2.4.9 # NoSQL data storage
|
||||||
path:
|
path:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user