mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
Refactoring / fixing
This commit is contained in:
parent
7373805ea7
commit
4080b4177b
30
lib/api.dart
30
lib/api.dart
@ -177,6 +177,8 @@ class InvenTreeAPI {
|
||||
* e.g. http://127.0.0.1:8000
|
||||
*/
|
||||
|
||||
if (profile == null) return false;
|
||||
|
||||
String address = profile.server.trim();
|
||||
String username = profile.username.trim();
|
||||
String password = profile.password.trim();
|
||||
@ -212,10 +214,13 @@ class InvenTreeAPI {
|
||||
print("Error connecting to server: ${error.toString()}");
|
||||
|
||||
if (error is SocketException) {
|
||||
showServerError(context, "Connection Refused");
|
||||
print("Error: socket exception: ${error.toString()}");
|
||||
// TODO - Display error dialog!!
|
||||
//showServerError(context, "Connection Refused");
|
||||
return null;
|
||||
} else if (error is TimeoutException) {
|
||||
showTimeoutDialog(context);
|
||||
// TODO - Display timeout dialog here
|
||||
//showTimeoutDialog(context);
|
||||
return null;
|
||||
} else {
|
||||
// Unknown error type - re-throw the error and Sentry will catch it
|
||||
@ -303,18 +308,25 @@ class InvenTreeAPI {
|
||||
};
|
||||
}
|
||||
|
||||
Future<bool> connectToServer(BuildContext context) async {
|
||||
print("InvenTreeAPI().connectToServer()");
|
||||
bool disconnectFromServer() {
|
||||
print("InvenTreeAPI().disconnectFromServer()");
|
||||
|
||||
// Clear connection flag
|
||||
_connected = false;
|
||||
|
||||
// Clear token
|
||||
_connecting = false;
|
||||
_token = '';
|
||||
profile = null;
|
||||
}
|
||||
|
||||
Future<bool> connectToServer(BuildContext context) async {
|
||||
|
||||
// Ensure server is first disconnected
|
||||
disconnectFromServer();
|
||||
|
||||
// Load selected profile
|
||||
profile = await UserProfileDBManager().getSelectedProfile();
|
||||
|
||||
print("API Profile: ${profile.toString()}");
|
||||
|
||||
if (profile == null) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
@ -326,13 +338,13 @@ class InvenTreeAPI {
|
||||
|
||||
_connecting = true;
|
||||
|
||||
_connect(context).then((result) {
|
||||
bool result = await _connect(context);
|
||||
|
||||
print("_connect() returned result: ${result}");
|
||||
|
||||
_connecting = false;
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Perform a PATCH request
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'package:InvenTree/widget/dialogs.dart';
|
||||
import 'package:InvenTree/widget/fields.dart';
|
||||
import 'package:InvenTree/widget/spinner.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@ -166,21 +168,20 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
|
||||
void _selectProfile(BuildContext context, UserProfile profile) async {
|
||||
|
||||
// Mark currently selected profile as unselected
|
||||
final selected = await UserProfileDBManager().getSelectedProfile();
|
||||
// Disconnect InvenTree
|
||||
InvenTreeAPI().disconnectFromServer();
|
||||
|
||||
selected.selected = false;
|
||||
|
||||
await UserProfileDBManager().updateProfile(selected);
|
||||
|
||||
profile.selected = true;
|
||||
|
||||
await UserProfileDBManager().updateProfile(profile);
|
||||
await UserProfileDBManager().selectProfile(profile.key);
|
||||
|
||||
_reload();
|
||||
|
||||
print("CONNECT FROM A");
|
||||
// Attempt server login (this will load the newly selected profile
|
||||
InvenTreeAPI().connectToServer(context);
|
||||
InvenTreeAPI().connectToServer(context).then((result) {
|
||||
_reload();
|
||||
});
|
||||
|
||||
_reload();
|
||||
}
|
||||
|
||||
void _deleteProfile(UserProfile profile) async {
|
||||
@ -191,6 +192,10 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
_reload();
|
||||
|
||||
if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) {
|
||||
InvenTreeAPI().disconnectFromServer();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateProfile(UserProfile profile) async {
|
||||
@ -201,6 +206,15 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
_reload();
|
||||
|
||||
if (InvenTreeAPI().isConnected() && profile.key == InvenTreeAPI().profile.key) {
|
||||
// Attempt server login (this will load the newly selected profile
|
||||
|
||||
print("Connect froM A");
|
||||
InvenTreeAPI().connectToServer(context).then((result) {
|
||||
_reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _addProfile(UserProfile profile) async {
|
||||
@ -213,11 +227,45 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
_reload();
|
||||
}
|
||||
|
||||
Widget _getProfileIcon(UserProfile profile) {
|
||||
|
||||
// Not selected? No icon for you!
|
||||
if (profile == null || !profile.selected) return null;
|
||||
|
||||
// Selected, but (for some reason) not the same as the API...
|
||||
if (InvenTreeAPI().profile == null || InvenTreeAPI().profile.key != profile.key) {
|
||||
return FaIcon(
|
||||
FontAwesomeIcons.questionCircle,
|
||||
color: Color.fromRGBO(250, 150, 50, 1)
|
||||
);
|
||||
}
|
||||
|
||||
// Reflect the connection status of the server
|
||||
if (InvenTreeAPI().isConnected()) {
|
||||
return FaIcon(
|
||||
FontAwesomeIcons.checkCircle,
|
||||
color: Color.fromRGBO(50, 250, 50, 1)
|
||||
);
|
||||
} else if (InvenTreeAPI().isConnecting()) {
|
||||
return Spinner(
|
||||
icon: FontAwesomeIcons.spinner,
|
||||
color: Color.fromRGBO(50, 50, 250, 1),
|
||||
);
|
||||
} else {
|
||||
return FaIcon(
|
||||
FontAwesomeIcons.timesCircle,
|
||||
color: Color.fromRGBO(250, 50, 50, 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
final Size screenSize = MediaQuery.of(context).size;
|
||||
|
||||
print("Building!");
|
||||
|
||||
List<Widget> children = [];
|
||||
|
||||
if (profiles != null && profiles.length > 0) {
|
||||
@ -228,10 +276,12 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
title: Text(
|
||||
profile.name,
|
||||
),
|
||||
subtitle: Text(profile.server),
|
||||
trailing: profile.selected
|
||||
? FaIcon(FontAwesomeIcons.checkCircle)
|
||||
: null,
|
||||
tileColor: profile.selected ? Color.fromRGBO(0, 0, 0, 0.05) : null,
|
||||
subtitle: Text("${profile.server}"),
|
||||
trailing: _getProfileIcon(profile),
|
||||
onTap: () {
|
||||
_selectProfile(context, profile);
|
||||
},
|
||||
onLongPress: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -239,50 +289,40 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
||||
return SimpleDialog(
|
||||
title: Text(profile.name),
|
||||
children: <Widget>[
|
||||
Divider(),
|
||||
SimpleDialogOption(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_selectProfile(context, profile);
|
||||
},
|
||||
child: Text(I18N
|
||||
.of(context)
|
||||
.profileSelect),
|
||||
child: Text(I18N.of(context).profileSelect),
|
||||
),
|
||||
SimpleDialogOption(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_editProfile(context, userProfile: profile);
|
||||
},
|
||||
child: Text(I18N
|
||||
.of(context)
|
||||
.profileEdit),
|
||||
child: Text(I18N.of(context).profileEdit),
|
||||
),
|
||||
SimpleDialogOption(
|
||||
onPressed: () {
|
||||
// Navigator.of(context, rootNavigator: true).pop();
|
||||
confirmationDialog(
|
||||
context,
|
||||
I18N
|
||||
.of(context)
|
||||
.delete,
|
||||
I18N.of(context).delete,
|
||||
"Delete this profile?",
|
||||
onAccept: () {
|
||||
_deleteProfile(profile);
|
||||
}
|
||||
);
|
||||
},
|
||||
child: Text(I18N
|
||||
.of(context)
|
||||
.profileDelete),
|
||||
child: Text(I18N.of(context).profileDelete),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
onTap: () {
|
||||
|
||||
},
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
@ -36,13 +36,13 @@ class UserProfile {
|
||||
// User ID (will be provided by the server on log-in)
|
||||
int user_id;
|
||||
|
||||
factory UserProfile.fromJson(int key, Map<String, dynamic> json) => UserProfile(
|
||||
factory UserProfile.fromJson(int key, Map<String, dynamic> json, bool isSelected) => UserProfile(
|
||||
key: key,
|
||||
name: json['name'],
|
||||
server: json['server'],
|
||||
username: json['username'],
|
||||
password: json['password'],
|
||||
selected: json['selected'] ?? false,
|
||||
selected: isSelected,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@ -50,33 +50,40 @@ class UserProfile {
|
||||
"server": server,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"selected": selected,
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "${server} - ${username}:${password}";
|
||||
return "<${key}> ${name} : ${server} - ${username}:${password}";
|
||||
}
|
||||
}
|
||||
|
||||
class UserProfileDBManager {
|
||||
|
||||
static const String folder_name = "profiles";
|
||||
|
||||
final _folder = intMapStoreFactory.store(folder_name);
|
||||
final store = StoreRef("profiles");
|
||||
|
||||
Future<Database> get _db async => await InvenTreePreferencesDB.instance.database;
|
||||
|
||||
Future<bool> profileNameExists(String name) async {
|
||||
|
||||
final finder = Finder(filter: Filter.equals("name", name));
|
||||
|
||||
final profiles = await store.find(await _db, finder: finder);
|
||||
|
||||
return profiles.length > 0;
|
||||
}
|
||||
|
||||
Future addProfile(UserProfile profile) async {
|
||||
|
||||
UserProfile existingProfile = await getProfile(profile.name);
|
||||
// Check if a profile already exists with the name
|
||||
final bool exists = await profileNameExists(profile.name);
|
||||
|
||||
if (existingProfile != null) {
|
||||
if (exists) {
|
||||
print("UserProfile '${profile.name}' already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
int key = await _folder.add(await _db, profile.toJson());
|
||||
int key = await store.add(await _db, profile.toJson());
|
||||
|
||||
print("Added user profile <${key}> - '${profile.name}'");
|
||||
|
||||
@ -84,23 +91,34 @@ class UserProfileDBManager {
|
||||
profile.key = key;
|
||||
}
|
||||
|
||||
Future selectProfile(int key) async {
|
||||
/*
|
||||
* Mark the particular profile as selected
|
||||
*/
|
||||
|
||||
final result = await store.record("selected").put(await _db, key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future updateProfile(UserProfile profile) async {
|
||||
|
||||
if (profile.key == null) {
|
||||
addProfile(profile);
|
||||
await addProfile(profile);
|
||||
return;
|
||||
}
|
||||
|
||||
final finder = Finder(filter: Filter.byKey(profile.key));
|
||||
await _folder.update(await _db, profile.toJson(), finder: finder);
|
||||
final result = await store.record(profile.key).update(await _db, profile.toJson());
|
||||
|
||||
print("Updated user profile <${profile.key}> - '${profile.name}");
|
||||
print("Updated user profile <${profile.key}> - '${profile.name}'");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future deleteProfile(UserProfile profile) async {
|
||||
final finder = Finder(filter: Filter.equals("name", profile.name));
|
||||
await _folder.delete(await _db, finder: finder);
|
||||
|
||||
await store.record(profile.key).delete(await _db);
|
||||
print("Deleted user profile <${profile.key}> - '${profile.name}'");
|
||||
}
|
||||
|
||||
@ -108,78 +126,50 @@ class UserProfileDBManager {
|
||||
/*
|
||||
* Return the currently selected profile.
|
||||
*
|
||||
* If multiple profiles are selected,
|
||||
* mark all but the first as unselected
|
||||
*
|
||||
* If no profile is currently selected,
|
||||
* then force the first profile to be selected.
|
||||
* key should match the "selected" property
|
||||
*/
|
||||
|
||||
final selected_finder = Finder(filter: Filter.equals("selected", true));
|
||||
final selected = await store.record("selected").get(await _db);
|
||||
|
||||
final selected_profiles = await _folder.find(await _db, finder: selected_finder);
|
||||
final profiles = await store.find(await _db);
|
||||
|
||||
if (selected_profiles.length == 1) {
|
||||
// A single profile is selected
|
||||
return UserProfile.fromJson(selected_profiles[0].key, selected_profiles[0].value);
|
||||
} else if (selected_profiles.length > 1) {
|
||||
// Multiple selected profiles - de-select others
|
||||
for (int idx = 1; idx < selected_profiles.length; idx++) {
|
||||
UserProfile profile = UserProfile.fromJson(selected_profiles[idx].key, selected_profiles[idx].value);
|
||||
List<UserProfile> profileList = new List<UserProfile>();
|
||||
|
||||
profile.selected = false;
|
||||
updateProfile(profile);
|
||||
for (int idx = 0; idx < profiles.length; idx++) {
|
||||
|
||||
if (profiles[idx].key is int && profiles[idx].key == selected) {
|
||||
return UserProfile.fromJson(
|
||||
profiles[idx].key,
|
||||
profiles[idx].value,
|
||||
profiles[idx].key == selected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// And return the first profile
|
||||
return UserProfile.fromJson(selected_profiles[0].key, selected_profiles[0].value);
|
||||
} else {
|
||||
// No profiles selected!
|
||||
|
||||
final all_profiles = await getAllProfiles();
|
||||
|
||||
if (all_profiles.length == 0) {
|
||||
// No profiles available
|
||||
return null;
|
||||
} else {
|
||||
UserProfile prf = all_profiles[0];
|
||||
prf.selected = true;
|
||||
updateProfile(prf);
|
||||
|
||||
// Return the selected profile
|
||||
return prf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<UserProfile> getProfile(String name) async {
|
||||
|
||||
print("Looking for user profile '${name}'");
|
||||
|
||||
// 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) {
|
||||
print("No matching profiles found");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the first matching profile object
|
||||
return UserProfile.fromJson(profiles[0].key, profiles[0].value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return all user profile objects
|
||||
*/
|
||||
Future<List<UserProfile>> getAllProfiles() async {
|
||||
final profiles = await _folder.find(await _db);
|
||||
|
||||
final selected = await store.record("selected").get(await _db);
|
||||
|
||||
final profiles = await store.find(await _db);
|
||||
|
||||
List<UserProfile> profileList = new List<UserProfile>();
|
||||
|
||||
for (int idx = 0; idx < profiles.length; idx++) {
|
||||
profileList.add(UserProfile.fromJson(profiles[idx].key, profiles[idx].value));
|
||||
|
||||
if (profiles[idx].key is int) {
|
||||
profileList.add(
|
||||
UserProfile.fromJson(
|
||||
profiles[idx].key,
|
||||
profiles[idx].value,
|
||||
profiles[idx].key == selected,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return profileList;
|
||||
|
@ -15,6 +15,7 @@ import 'package:InvenTree/widget/category_display.dart';
|
||||
import 'package:InvenTree/widget/company_list.dart';
|
||||
import 'package:InvenTree/widget/location_display.dart';
|
||||
import 'package:InvenTree/widget/search.dart';
|
||||
import 'package:InvenTree/widget/spinner.dart';
|
||||
import 'package:InvenTree/widget/drawer.dart';
|
||||
|
||||
class InvenTreeHomePage extends StatefulWidget {
|
||||
@ -28,19 +29,10 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
|
||||
_InvenTreeHomePageState() : super() {
|
||||
|
||||
// Initially load the profile and attempt server connection
|
||||
_loadProfile();
|
||||
}
|
||||
|
||||
String _serverStatus = "Connecting to server";
|
||||
|
||||
String _serverMessage = "";
|
||||
|
||||
bool _serverConnection = false;
|
||||
|
||||
FaIcon _serverIcon = new FaIcon(FontAwesomeIcons.spinner);
|
||||
|
||||
Color _serverStatusColor = Color.fromARGB(255, 50, 50, 250);
|
||||
|
||||
// Selected user profile
|
||||
UserProfile _profile;
|
||||
|
||||
@ -111,30 +103,20 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
|
||||
void _loadProfile() async {
|
||||
|
||||
final profile = await UserProfileDBManager().getSelectedProfile();
|
||||
_profile = await UserProfileDBManager().getSelectedProfile();
|
||||
|
||||
// If a different profile is selected, re-connect
|
||||
if (_profile == null || (_profile.key != profile.key)) {
|
||||
|
||||
if (_context != null) {
|
||||
print("Connecting Profile: ${profile.name} - ${profile.server}");
|
||||
// A valid profile was loaded!
|
||||
if (_profile != null) {
|
||||
if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) {
|
||||
|
||||
print("Connect from C");
|
||||
InvenTreeAPI().connectToServer(_context).then((result) {
|
||||
setState(() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
setState(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_profile = profile;
|
||||
|
||||
setState(() {
|
||||
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
ListTile _serverTile() {
|
||||
@ -162,10 +144,13 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
title: Text("Connecting to server..."),
|
||||
subtitle: Text("${InvenTreeAPI().baseUrl}"),
|
||||
leading: FaIcon(FontAwesomeIcons.server),
|
||||
trailing: FaIcon(
|
||||
FontAwesomeIcons.spinner,
|
||||
trailing: Spinner(
|
||||
icon: FontAwesomeIcons.spinner,
|
||||
color: Color.fromRGBO(50, 50, 250, 1),
|
||||
)
|
||||
),
|
||||
onTap: () {
|
||||
_selectProfile();
|
||||
}
|
||||
);
|
||||
} else if (InvenTreeAPI().isConnected()) {
|
||||
return ListTile(
|
||||
@ -196,42 +181,11 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
void onConnectSuccess(String msg) async {
|
||||
|
||||
final profile = await UserProfileDBManager().getSelectedProfile();
|
||||
|
||||
String address = profile?.server ?? 'unknown server address';
|
||||
|
||||
_serverConnection = true;
|
||||
_serverMessage = msg;
|
||||
_serverStatus = "Connected to ${address}";
|
||||
_serverStatusColor = Color.fromARGB(255, 50, 250, 50);
|
||||
_serverIcon = new FaIcon(FontAwesomeIcons.checkCircle, color: _serverStatusColor);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void onConnectFailure(String msg) async {
|
||||
|
||||
final profile = await UserProfileDBManager().getSelectedProfile();
|
||||
|
||||
_serverConnection = false;
|
||||
_serverMessage = msg;
|
||||
_serverStatus = "Could not connect to ${profile?.server}";
|
||||
_serverStatusColor = Color.fromARGB(255, 250, 50, 50);
|
||||
_serverIcon = new FaIcon(FontAwesomeIcons.timesCircle, color: _serverStatusColor);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
_context = context;
|
||||
|
||||
_loadProfile();
|
||||
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
|
54
lib/widget/spinner.dart
Normal file
54
lib/widget/spinner.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class Spinner extends StatefulWidget {
|
||||
final IconData icon;
|
||||
final Duration duration;
|
||||
final Color color;
|
||||
|
||||
const Spinner({
|
||||
this.color = const Color.fromRGBO(150, 150, 150, 1),
|
||||
Key key,
|
||||
@required this.icon,
|
||||
this.duration = const Duration(milliseconds: 1800),
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SpinnerState createState() => _SpinnerState();
|
||||
}
|
||||
|
||||
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
|
||||
AnimationController _controller;
|
||||
Widget _child;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: Duration(milliseconds: 2000),
|
||||
)
|
||||
..repeat();
|
||||
_child = FaIcon(
|
||||
widget.icon,
|
||||
color: widget.color
|
||||
);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RotationTransition(
|
||||
turns: _controller,
|
||||
child: _child,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user