From b69762ff1575a4a19dc7b2a6e6b9eb7b9bffb925 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 2 Feb 2021 20:37:54 +1100 Subject: [PATCH 1/4] API refactoring: - Error if the server version is *older* than the min required version - Display dialog boxes for different server errors --- lib/api.dart | 136 +++++++++++++++++++++++++++------------- lib/barcode.dart | 1 - lib/l10n | 2 +- lib/main.dart | 4 +- lib/preferences.dart | 9 +-- lib/settings/login.dart | 35 ++++++----- lib/widget/dialogs.dart | 15 ++++- lib/widget/home.dart | 7 +-- 8 files changed, 133 insertions(+), 76 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index f947655f..29e2e749 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -2,11 +2,15 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:image/image.dart'; +import 'package:InvenTree/widget/dialogs.dart'; + import 'package:path/path.dart' as path; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; @@ -22,18 +26,56 @@ import 'package:shared_preferences/shared_preferences.dart'; class InvenTreeAPI { - // Minimum supported InvenTree server version is 0.1.1 - static const List MIN_SUPPORTED_VERSION = [0, 1, 1]; + // Minimum supported InvenTree server version is + static const List MIN_SUPPORTED_VERSION = [0, 1, 5]; + + String get _requiredVersionString => "${MIN_SUPPORTED_VERSION[0]}.${MIN_SUPPORTED_VERSION[1]}.${MIN_SUPPORTED_VERSION[2]}"; bool _checkServerVersion(String version) { - // TODO - Decode the provided version string and determine if the server is "new" enough - return false; + + // Provided version string should be of the format "x.y.z [...]" + List versionSplit = version.split(' ').first.split('.'); + + // Extract the version number .. from the string + if (versionSplit.length != 3) { + return false; + } + + // Cast the server version to an explicit integer + int server_version_code = 0; + + print("server version: ${version}"); + + server_version_code += (int.tryParse(versionSplit[0]) ?? 0) * 100 * 100; + server_version_code += (int.tryParse(versionSplit[1]) ?? 0) * 100; + server_version_code += (int.tryParse(versionSplit[2])); + + print("server version code: ${server_version_code}"); + + int required_version_code = 0; + + required_version_code += MIN_SUPPORTED_VERSION[0] * 100 * 100; + required_version_code += MIN_SUPPORTED_VERSION[1] * 100; + required_version_code += MIN_SUPPORTED_VERSION[2]; + + print("required version code: ${required_version_code}"); + + return server_version_code >= required_version_code; } // Endpoint for requesting an API token static const _URL_GET_TOKEN = "user/token/"; static const _URL_GET_VERSION = ""; + Future showServerError(BuildContext context, String description) async { + showErrorDialog( + context, + I18N.of(context).serverError, + description, + icon: FontAwesomeIcons.server + ); + } + // Base URL for InvenTree API e.g. http://192.168.120.10:8000 String _BASE_URL = ""; @@ -58,21 +100,13 @@ class InvenTreeAPI { return baseUrl + url; } - String get apiUrl { - return _makeUrl("/api/"); - } + String get apiUrl => _makeUrl("/api/"); - String get imageUrl { - return _makeUrl("/image/"); - } + String get imageUrl => _makeUrl("/image/"); - String makeApiUrl(String endpoint) { - return _makeUrl("/api/" + endpoint); - } + String makeApiUrl(String endpoint) => _makeUrl("/api/" + endpoint); - String makeUrl(String endpoint) { - return _makeUrl(endpoint); - } + String makeUrl(String endpoint) => _makeUrl(endpoint); String _username = ""; String _password = ""; @@ -80,9 +114,7 @@ class InvenTreeAPI { // Authentication token (initially empty, must be requested) String _token = ""; - bool isConnected() { - return _token.isNotEmpty; - } + bool isConnected() => _token.isNotEmpty; /* * Check server connection and display messages if not connected. @@ -106,9 +138,6 @@ class InvenTreeAPI { return false; } - // Is the server version too old? - // TODO - // Finally return true; } @@ -138,18 +167,17 @@ class InvenTreeAPI { InvenTreeAPI._internal(); - Future connect() async { + Future connect(BuildContext context) async { var prefs = await SharedPreferences.getInstance(); String server = prefs.getString("server"); String username = prefs.getString("username"); String password = prefs.getString("password"); - return connectToServer(server, username, password); + return connectToServer(context, server, username, password); } - Future connectToServer(String address, String username, - String password) async { + Future connectToServer(BuildContext context, String address, String username, String password) async { /* Address is the base address for the InvenTree server, * e.g. http://127.0.0.1:8000 @@ -161,9 +189,14 @@ class InvenTreeAPI { username = username.trim(); if (address.isEmpty || username.isEmpty || password.isEmpty) { - errorMessage = "Server Error: Empty details supplied"; - print(errorMessage); - throw errorMessage; + await showErrorDialog( + context, + I18N.of(context).error, + "Incomplete server details", + icon: FontAwesomeIcons.server + ); + + return false; } if (!address.endsWith('/')) { @@ -185,29 +218,34 @@ class InvenTreeAPI { print("Connecting to " + apiUrl + " -> " + username + ":" + password); - // TODO - Add connection timeout - var response = await get("").timeout(Duration(seconds: 10)).catchError((error) { if (error is SocketException) { - print("Could not connect to server"); + errorMessage = "Could not connect to server"; return null; } else if (error is TimeoutException) { - print("Server timeout"); + errorMessage = "Server timeout"; return null; } else { + // Unknown error type + errorMessage = error.toString(); // Unknown error type, re-throw error - print("Unknown error: ${error.toString()}"); - throw error; + return null; } }); if (response == null) { + // Null (or error) response: Show dialog and exit + + await showServerError(context, errorMessage); return false; } if (response.statusCode != 200) { - print("Invalid status code: " + response.statusCode.toString()); + // Any status code other than 200! + + // TODO: Interpret the error codes and show custom message? + await showServerError(context, "Invalid response code: ${response.statusCode.toString()}"); return false; } @@ -217,9 +255,9 @@ class InvenTreeAPI { // We expect certain response from the server if (!data.containsKey("server") || !data.containsKey("version")) { - errorMessage = "Server resonse contained incorrect data"; - print(errorMessage); - throw errorMessage; + + await showServerError(context, "Server response missing required fields"); + return false; } print("Server: " + data["server"]); @@ -228,35 +266,43 @@ class InvenTreeAPI { _version = data["version"]; if (!_checkServerVersion(_version)) { - // TODO - Something? + await showServerError(context, "Server version is too old.\n\nServer Version: ${_version}\n\nRequired version: ${_requiredVersionString}"); + return false; } // Record the instance name of the server instance = data['instance'] ?? ''; // Request token from the server if we do not already have one - if (_token.isNotEmpty) { + if (false && _token.isNotEmpty) { print("Already have token - $_token"); return true; } - // Clear out the token + // Clear the existing token value _token = ""; + print("Requesting token from server"); + response = await get(_URL_GET_TOKEN).timeout(Duration(seconds: 10)).catchError((error) { print("Error requesting token:"); print(error); - return false; + return null; }); + if (response == null) { + await showServerError(context, "Error requesting access token"); + return false; + } + if (response.statusCode != 200) { - print("Invalid status code: " + response.statusCode.toString()); + await showServerError(context, "Invalid status code: ${response.statusCode.toString()}"); return false; } else { var data = json.decode(response.body); if (!data.containsKey("token")) { - print("No token provided in response"); + await showServerError(context, "No token provided in response"); return false; } diff --git a/lib/barcode.dart b/lib/barcode.dart index f92c9ae7..a17b8091 100644 --- a/lib/barcode.dart +++ b/lib/barcode.dart @@ -32,7 +32,6 @@ class BarcodeHandler { QRViewController _controller; BuildContext _context; - Future onBarcodeMatched(Map data) { // Called when the server "matches" a barcode // Override this function diff --git a/lib/l10n b/lib/l10n index 6b50ee70..8f8809c1 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 6b50ee704cacfb3d9b45d89de31b1ce05e94959a +Subproject commit 8f8809c18776e8bfac050c35ad17e0416af96ad4 diff --git a/lib/main.dart b/lib/main.dart index 46b31b85..2c0e410a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,9 +45,6 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); - // Load login details - InvenTreePreferences().loadLoginDetails(); - runZoned>(() async { runApp(InvenTreeApp()); }, onError: (error, stackTrace) { @@ -63,6 +60,7 @@ class InvenTreeApp extends StatelessWidget { @override Widget build(BuildContext context) { + return MaterialApp( onGenerateTitle: (BuildContext context) => I18N.of(context).appTitle, theme: ThemeData( diff --git a/lib/preferences.dart b/lib/preferences.dart index 158224d6..91ced0b2 100644 --- a/lib/preferences.dart +++ b/lib/preferences.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'api.dart'; @@ -35,7 +36,7 @@ class InvenTreePreferences { InvenTreePreferences._internal(); // Load saved login details, and attempt connection - void loadLoginDetails() async { + void loadLoginDetails(BuildContext context) async { print("Loading login details"); @@ -45,10 +46,10 @@ class InvenTreePreferences { var username = prefs.getString(_USERNAME) ?? ''; var password = prefs.getString(_PASSWORD) ?? ''; - await InvenTreeAPI().connectToServer(server, username, password); + await InvenTreeAPI().connectToServer(context, server, username, password); } - void saveLoginDetails(String server, String username, String password) async { + void saveLoginDetails(BuildContext context, String server, String username, String password) async { SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -57,6 +58,6 @@ class InvenTreePreferences { await prefs.setString(_PASSWORD, password); // Reconnect the API - await InvenTreeAPI().connectToServer(server, username, password); + await InvenTreeAPI().connectToServer(context, server, username, password); } } \ No newline at end of file diff --git a/lib/settings/login.dart b/lib/settings/login.dart index 15ae6db0..79aa2c89 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../api.dart'; import '../preferences.dart'; @@ -61,6 +62,15 @@ class _InvenTreeLoginSettingsState extends State { return null; } + void _save(BuildContext context) async { + if (_formKey.currentState.validate()) { + _formKey.currentState.save(); + + await InvenTreePreferences().saveLoginDetails(context, _server, _username, _password); + + } + } + @override Widget build(BuildContext context) { @@ -76,7 +86,7 @@ class _InvenTreeLoginSettingsState extends State { key: _formKey, child: new ListView( children: [ - Text("Server Address"), + Text(I18N.of(context).serverAddress), new TextFormField( initialValue: _server, decoration: InputDecoration( @@ -92,8 +102,8 @@ class _InvenTreeLoginSettingsState extends State { TextFormField( initialValue: _username, decoration: InputDecoration( - hintText: "Username", - labelText: "Username", + hintText: I18N.of(context).username, + labelText: I18N.of(context).username, ), validator: _validateUsername, onSaved: (String value) { @@ -104,8 +114,8 @@ class _InvenTreeLoginSettingsState extends State { initialValue: _password, obscureText: true, decoration: InputDecoration( - hintText: "Password", - labelText: "Password", + hintText: I18N.of(context).password, + labelText: I18N.of(context).password, ), validator: _validatePassword, onSaved: (String value) { @@ -115,8 +125,10 @@ class _InvenTreeLoginSettingsState extends State { Container( width: screenSize.width, child: RaisedButton( - child: Text("Save"), - onPressed: this.save, + child: Text(I18N.of(context).save), + onPressed: () { + _save(context); + } ) ) ], @@ -125,13 +137,4 @@ class _InvenTreeLoginSettingsState extends State { ) ); } - - void save() async { - if (_formKey.currentState.validate()) { - _formKey.currentState.save(); - - await InvenTreePreferences().saveLoginDetails(_server, _username, _password); - - } - } } \ No newline at end of file diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index 5efaa892..1816df20 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; void showMessage(BuildContext context, String message) { Scaffold.of(context).showSnackBar(SnackBar( @@ -9,7 +10,12 @@ void showMessage(BuildContext context, String message) { )); } -Future showInfoDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.info, String info = "Info", Function onDismissed}) async { +Future showInfoDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.info, String info, Function onDismissed}) async { + + if (info == null || info.isEmpty) { + info = I18N.of(context).info; + } + showDialog( context: context, child: SimpleDialog( @@ -31,7 +37,12 @@ Future showInfoDialog(BuildContext context, String title, String descripti }); } -Future showErrorDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error = "Error", Function onDismissed}) async { +Future showErrorDialog(BuildContext context, String title, String description, {IconData icon = FontAwesomeIcons.exclamationCircle, String error, Function onDismissed}) async { + + if (error == null || error.isEmpty) { + error = I18N.of(context).error; + } + showDialog( context: context, child: SimpleDialog( diff --git a/lib/widget/home.dart b/lib/widget/home.dart index 73c28cb3..0a2d0129 100644 --- a/lib/widget/home.dart +++ b/lib/widget/home.dart @@ -25,7 +25,6 @@ class InvenTreeHomePage extends StatefulWidget { class _InvenTreeHomePageState extends State { _InvenTreeHomePageState() : super() { - _checkServerConnection(); } String _serverAddress = ""; @@ -63,7 +62,7 @@ class _InvenTreeHomePageState extends State { /* * Test the server connection */ - void _checkServerConnection() async { + void _checkServerConnection(BuildContext context) async { var prefs = await SharedPreferences.getInstance(); @@ -76,7 +75,7 @@ class _InvenTreeHomePageState extends State { _serverIcon = new FaIcon(FontAwesomeIcons.spinner); _serverStatusColor = Color.fromARGB(255, 50, 50, 250); - InvenTreeAPI().connect().then((bool result) { + InvenTreeAPI().connect(context).then((bool result) { if (result) { onConnectSuccess(""); @@ -322,7 +321,7 @@ class _InvenTreeHomePageState extends State { leading: _serverIcon, onTap: () { if (!_serverConnection) { - _checkServerConnection(); + _checkServerConnection(context); } }, ), From 1823dca61d533e1bb933fe787b382955f1c09439 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 2 Feb 2021 20:50:10 +1100 Subject: [PATCH 2/4] Update for settings menus --- lib/l10n | 2 +- lib/settings/about.dart | 27 +++++++++++++-------------- lib/settings/settings.dart | 12 ++++++++++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/l10n b/lib/l10n index 8f8809c1..0b7d0314 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 8f8809c18776e8bfac050c35ad17e0416af96ad4 +Subproject commit 0b7d0314cb27fd51422836d3b0dcb3285689898a diff --git a/lib/settings/about.dart b/lib/settings/about.dart index ad812183..e0b07107 100644 --- a/lib/settings/about.dart +++ b/lib/settings/about.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:package_info/package_info.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class InvenTreeAboutWidget extends StatelessWidget { @@ -21,11 +22,14 @@ class InvenTreeAboutWidget extends StatelessWidget { body: ListView( children: [ ListTile( - title: Text("Server Address"), + title: Text(I18N.of(context).serverDetails), + ), + ListTile( + title: Text(I18N.of(context).address), subtitle: Text(InvenTreeAPI().baseUrl.isNotEmpty ? InvenTreeAPI().baseUrl : "Not connected"), ), ListTile( - title: Text("Server Version"), + title: Text(I18N.of(context).version), subtitle: Text(InvenTreeAPI().version.isNotEmpty ? InvenTreeAPI().version : "Not connected"), ), ListTile( @@ -34,7 +38,10 @@ class InvenTreeAboutWidget extends StatelessWidget { ), Divider(), ListTile( - title: Text("App Name"), + title: Text(I18N.of(context).appDetails), + ), + ListTile( + title: Text(I18N.of(context).name), subtitle: Text("${info.appName}"), ), ListTile( @@ -42,20 +49,12 @@ class InvenTreeAboutWidget extends StatelessWidget { subtitle: Text("${info.packageName}"), ), ListTile( - title: Text("App Version"), + title: Text(I18N.of(context).version), subtitle: Text("${info.version}"), ), ListTile( - title: Text("Build Number"), - subtitle: Text("${info.buildNumber}") - ), - Divider(), - ListTile( - title: Text("Submit Bug Report"), - subtitle: Text("https://github.com/inventree/inventree-app/issues/"), - onTap: () { - // TODO - Open the URL in an external webpage? - }, + title: Text(I18N.of(context).build), + subtitle: Text("${info.buildNumber}"), ) ], ) diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index a3651e88..9802b489 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + import 'package:InvenTree/api.dart'; import 'login.dart'; @@ -37,11 +39,17 @@ class _InvenTreeSettingsState extends State { ), Divider(), ListTile( - title: Text("About"), - subtitle: Text("App details"), + title: Text(I18N.of(context).about), + subtitle: Text(I18N.of(context).appDetails), leading: FaIcon(FontAwesomeIcons.infoCircle), onTap: _about, ), + ListTile( + title: Text("Report Bug"), + subtitle: Text("Report bug or suggest new feature"), + leading: FaIcon(FontAwesomeIcons.bug), + onTap: null, + ), ], ) ) From c3bc6a94b08f9e84713b0c7cf4592c9d6603e779 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 2 Feb 2021 21:20:04 +1100 Subject: [PATCH 3/4] Display version information (using external markdown file) --- assets/release_notes.md | 3 +++ lib/settings/release.dart | 27 +++++++++++++++++++++++++++ lib/settings/settings.dart | 19 +++++++++++++++++++ pubspec.lock | 14 ++++++++++++++ pubspec.yaml | 5 +++-- 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 assets/release_notes.md create mode 100644 lib/settings/release.dart diff --git a/assets/release_notes.md b/assets/release_notes.md new file mode 100644 index 00000000..be9eeb77 --- /dev/null +++ b/assets/release_notes.md @@ -0,0 +1,3 @@ +## 0.1.0 (February 2021) + +- Initial app version release \ No newline at end of file diff --git a/lib/settings/release.dart b/lib/settings/release.dart new file mode 100644 index 00000000..df5c6099 --- /dev/null +++ b/lib/settings/release.dart @@ -0,0 +1,27 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:markdown/markdown.dart' as md; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + + +class ReleaseNotesWidget extends StatelessWidget { + + final String releaseNotes; + + ReleaseNotesWidget(this.releaseNotes); + + @override + Widget build (BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Release Notes") + ), + body: Markdown( + selectable: false, + data: releaseNotes, + ) + ); + } +} \ No newline at end of file diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index 9802b489..d12a0fee 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -1,7 +1,9 @@ import 'package:InvenTree/settings/about.dart'; import 'package:InvenTree/settings/login.dart'; +import 'package:InvenTree/settings/release.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -44,6 +46,12 @@ class _InvenTreeSettingsState extends State { leading: FaIcon(FontAwesomeIcons.infoCircle), onTap: _about, ), + ListTile( + title: Text("Release Notes"), + subtitle: Text("Display app release notes"), + leading: FaIcon(FontAwesomeIcons.fileAlt), + onTap: _releaseNotes, + ), ListTile( title: Text("Report Bug"), subtitle: Text("Report bug or suggest new feature"), @@ -70,4 +78,15 @@ class _InvenTreeSettingsState extends State { MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info))); }); } + + void _releaseNotes() async { + + // Load release notes from external file + String notes = await rootBundle.loadString("assets/release_notes.md"); + + Navigator.push( + context, + MaterialPageRoute(builder: (context) => ReleaseNotesWidget(notes)) + ); + } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 6569a05d..04026bf0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -179,6 +179,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_markdown: + dependency: "direct main" + description: + name: flutter_markdown + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.2" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -259,6 +266,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.16.1" + markdown: + dependency: transitive + description: + name: markdown + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ec09a4c1..ad7660c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: InvenTree stock management # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 0.1.0+1 environment: sdk: ">=2.1.0 <3.0.0" @@ -39,7 +39,7 @@ dependencies: flutter_typeahead: ^1.8.1 # Auto-complete input field image_picker: ^0.6.6 # Select or take photos url_launcher: ^5.7.10 # Open link in system browser - + flutter_markdown: ^0.5.2 # Rendering markdown camera: path_provider: path: @@ -69,6 +69,7 @@ flutter: assets: - assets/image/icon.png + - assets/release_notes.md # To add assets to your application, add an assets section, like this: # assets: From 18229fbfe277eddb0455e3520d6cf6a4cbe5aa32 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 2 Feb 2021 21:24:07 +1100 Subject: [PATCH 4/4] More translations --- lib/l10n | 2 +- lib/settings/login.dart | 2 +- lib/settings/settings.dart | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/l10n b/lib/l10n index 0b7d0314..58e2c502 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 0b7d0314cb27fd51422836d3b0dcb3285689898a +Subproject commit 58e2c5027b481a3a620c27b90ae20b888a02bd96 diff --git a/lib/settings/login.dart b/lib/settings/login.dart index 79aa2c89..dbe95461 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -98,7 +98,7 @@ class _InvenTreeLoginSettingsState extends State { }, ), Divider(), - Text("Account Details"), + Text(I18N.of(context).accountDetails), TextFormField( initialValue: _username, decoration: InputDecoration( diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index d12a0fee..a2cf1663 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -34,7 +34,7 @@ class _InvenTreeSettingsState extends State { child: ListView( children: [ ListTile( - title: Text("Server Settings"), + title: Text(I18N.of(context).serverSettings), subtitle: Text("Configure server and login settings"), leading: FaIcon(FontAwesomeIcons.server), onTap: _editServerSettings, @@ -47,13 +47,13 @@ class _InvenTreeSettingsState extends State { onTap: _about, ), ListTile( - title: Text("Release Notes"), + title: Text(I18N.of(context).releaseNotes), subtitle: Text("Display app release notes"), leading: FaIcon(FontAwesomeIcons.fileAlt), onTap: _releaseNotes, ), ListTile( - title: Text("Report Bug"), + title: Text(I18N.of(context).reportBug), subtitle: Text("Report bug or suggest new feature"), leading: FaIcon(FontAwesomeIcons.bug), onTap: null,